Вы находитесь на странице: 1из 37

Site - https://audacious-media-player.

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

Observação: Utilizar fontes de autores renomados ou artigos acadêmicos confiáveis. Anotar as


fontes para referências bibliográficas.

As imagens que farão parte do trabalho deverão ser printadas e coladas na pasta
compartilhada do Drive - Verifique seu Drive

As imagens eu estarei modificando no CorelDraw para não copiarmos direto.

Quais mecanismos de sincronização utilizados pelo Audacious.

Explique o funcionamento das threads no Audacious e os mecanismos de sincronização.

Explique o funcionamento dos mesmos no código do aplicativo.

Criar as imagens que farão parte do site no Corel.

Dontpads de cada um:

http://dontpad.com/MedeirosAudacious

http://dontpad.com/PaulosiAudacious

http://dontpad.com/MagalhaesAudacious

MÃO NA MASSA VAMOS DETONAR NESSE TRABALHO!!!!

DESCREVA AQUI OS TEMAS QUE VOCÊ IRÁ ESCREVER PARA SEREM COPIADAS PARA O SITE
DEPOIS:

<h1>Funcionamento de Threads no programa Audacious e os


mecanismos de sincronização.</h1>

<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.

"Desta forma, as threads podem ser entendidas como fluxos independentes de


execução pertencentes a um mesmo processo, que requerem menos recursos de controle por
parte do sistema operacional" (Jandl 2004).

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.

* Threads de um mesmo processo possuem compartilhamento de recursos


simplificado entre elas.

* Possuem uma economia com o uso de estruturas de controle reduzidas em


comparação ao controle típico dos processos. E também o uso de compartilhamento de
recursos simplificado também gera economia de outros recursos.

* Torna possível a exploração de diversas arquiteturas computacionais que possuem


múltiplos processadores com uma maior adequabilidade. </p>

<h2>O uso de mecanismos de sincronização no software Audacious</h2>


<p>O programa Audacious faz uso de vários mecanismos para o controle de execução
nos SO's, geralmente traz em suas linhas de códigos ferramentas como mecanismos de
interrupções, exclusões mutuas e bloqueios de execução. </p>

<h2>O uso de ferramentas de sincronização no Reprodutor Audacious</h2>


<p>Os códigos apresentados estarão atentados apenas para assuntos relevantes e
específicos quanto ao uso de threads, funções de exclusões mútuas, monitores, semáforos e
funções de sincronização presentes neste player de música. </p>

<h3>Análise do código ggettext.c.diff</h3>


<p>Em várias etapas do código fonte do programa Audacious é possível encontrar
diversas utilizações de threads, o que mostra que sua utilização traz grandes vantagens para o
processamento das tarefas em execução, já que se trata de um player de áudio é
extremamente necessário que exista uma sincronização para que exista qualidade na
reprodução dos áudios.

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>

<h3>Sequência de código ggettext.c.diff</h3>

<pre><code class="c++">--- ggettext.c.0 2013-10-25 11:58:38 -0400


+++ ggettext.c 2014-03-23 22:07:29 -0400
@@ -94,17 +94,32 @@
#endif /* G_OS_WIN32 */

-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;

return g_dgettext (GETTEXT_PACKAGE, str);


}
@@ -152,7 +170,8 @@
glib_pgettext (const gchar *msgctxtid,
gsize msgidoffset)
{
- ensure_gettext_initialized ();
+ if (G_UNLIKELY (!ensure_gettext_initialized ()))
+ return msgctxtid;

return g_dpgettext (GETTEXT_PACKAGE, msgctxtid, msgidoffset);


}</pre></code>

<p>A sequência de código acima relacionada faz uso da linguagem C, o programa


chama uma função static gboolean ensure_gettext_initialized (void). Em C, por padrão as
funções são globais, a palavra chave static antes de uma declaração de função a torna estática.
Por exemplo a função abaixo função() é estática. <p>

<pre><code class="c++">static int funcao(void)


{
printf("Eu sou uma funcao estatica");
}</pre></code>
<p>Diferentemente das funções globais na linguagem C o acesso a funções estáticas é
restrito ao arquivo onde elas são declaradas. Portanto quando queremos limitar o acesso a
funções, definimos elas como estáticas.

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.

Em seguida é definido um ponteiro GThread, que representa um encadeamento em


execução. O ponteiro criado *init_thread_atomic será utilizado. O conceito de operações
atômicas é utilizado quando se quer evitar as chamadas race conditions. As operações
atômicas são operações que não podem ser interrompidas. Operações atômicas são relevantes
em outras áreas além de Sistemas Operacionais:

*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;

*Esta área trata de problemas de coordenação de acessos múltiplos e concorrentes a


bancos de dados.

*Bancos eletrônicos são uma das aplicações importantes da área.

Continuando a análise temos a seguinte declaração: </p>

<pre><code class="c++">if (G_UNLIKELY (init_thread && init_thread == g_thread_self ()))


return FALSE; </pre></code>

<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++"> if (g_once_init_enter (&initialised))


{
#ifdef G_OS_WIN32//se estiver definido no header será executada a sequência abaixo
- 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;
}</pre></code>
<p>A sequência acima define um ponteiro de caractere C padrão *tmp que recebe
valores passados pela função externa _glib_get_locale_dir () pertencente ao Win32 e será
utilizado em alguns trechos do código para que o arquivo compilado seja padronizado para o
SO que irá ser executado.

A função g_atomic_pointer_set (&init_thread_atomic, g_thread_self ()) é uma função


que não deve ser interrompida no instante de execução e tem como finalidade manipular os
valores passados por parâmetro do endereço de ponteiro de init_thread_atomic utilizando
chamada por referência e o valor retornado pela função g_thread_self().

A função bindtextdomain ( string domínio, string iretorio), configura um caminho para


um domínio. Esta função está como condicional se caso não estiver definido a variável
G_OS_WIN32 colocando a mesma em execução. Ela utiliza as variáveis externas definidas
GETTEXT_PACKAGE que passa o valor do domínio, e a variável também externa
GLIB_LOCALE_DIR que contém o valor do diretório.

Ao final da sequência deste código temos a função g_once_init_leave() que é uma


função linux, esta é uma contrapartida para a função g_once_init_enter(). Espera um local de
uma variável de inicialização com inicialização 0 estática e um valor de inicialização diferente
de 0. Define a variável como o valor de inicialização e libera o bloqueio simultâneo de threads
g_once_init_enter() nessa variável de inicialização. Esta função passa como parâmetros a
localização de uma variável estática contendo 0 e o resultado novo valor diferente de 0 para
*value_location. </p>

<h3>Análise do código adder.c</h3>


<p>A seguir temos uma sequência de código do programa adder.c onde estará sendo
observado os usos de ferramentas de sincronização do Audacious. </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.

If we already have metadata, or the file is itself a subtune, then


neither of these reasons apply.
*/
if (! item.tuple.valid () && ! is_subtune (item.filename))
{
/* If we open the file to identify the decoder, we can re-use the same
handle to read metadata. */
VFSFile file;
if (! item.decoder)
{
if (aud_get_bool (nullptr, "slow_probe"))
{
/* The slow path. User settings dictate that we should try to
find a decoder even if we don't recognize the file extension. */
item.decoder = aud_file_find_decoder (item.filename, false, file);
if (skip_invalid && ! item.decoder)
return;
}
else
{
/* The fast path. First see whether any plugins recognize the
file extension. Note that it's possible for multiple plugins
to recognize the same extension (.ogg, for example). */
int flags = probe_by_filename (item.filename);
if (skip_invalid && ! (flags & PROBE_FLAG_HAS_DECODER))
return;
if ((flags & PROBE_FLAG_MIGHT_HAVE_SUBTUNES))
{
/* At least one plugin recognized the file extension and
indicated that there might be subtunes. Figure out for
sure which decoder we need to use for this file. */
item.decoder = aud_file_find_decoder (item.filename, true, file);
if (skip_invalid && ! item.decoder)
return;
}
}
}
/* At this point we've either identified the decoder or determined that
the file doesn't have any subtunes. If the former, read the tag so
so we can expand any subtunes we find. */
if (item.decoder && input_plugin_has_subtunes (item.decoder))
aud_file_read_tag (item.filename, item.decoder, file, item.tuple);
}
int n_subtunes = item.tuple.get_n_subtunes ();
if (n_subtunes)
{
for (int sub = 0; sub < n_subtunes; sub ++)
{
StringBuf subname = str_printf ("%s?%d",
(const char *) item.filename, item.tuple.get_nth_subtune (sub));
if (! filter || filter (subname, user))
add_file ({String (subname), Tuple (), item.decoder}, filter, user, result, false);
else
result->filtered = true;
}
}
else
result->items.append (std::move (item));
}
/* To prevent infinite recursion, we currently allow adding a folder from within
a playlist, but not a playlist from within a folder, nor a second playlist
from within a playlist (this last rule is enforced by setting
<from_playlist> to true from within add_playlist()). */
static void add_generic (PlaylistAddItem && item, Playlist::FilterFunc filter,
void * user, AddResult * result, bool save_title, bool from_playlist);
static void add_playlist (const char * filename, Playlist::FilterFunc filter,
void * user, AddResult * result, bool save_title)
{
AUDINFO ("Adding playlist: %s\n", filename);
status_update (filename, result->items.len ());
String title;
Index<PlaylistAddItem> items;
if (! playlist_load (filename, title, items))
return;
if (save_title)
result->title = title;
for (auto & item : items)
add_generic (std::move (item), filter, user, result, false, true);
}
static void add_cuesheets (Index<String> & files, Playlist::FilterFunc filter,
void * user, AddResult * result)
{
Index<String> cuesheets;
for (int i = 0; i < files.len ();)
{
if (str_has_suffix_nocase (files[i], ".cue"))
cuesheets.move_from (files, i, -1, 1, true, true);
else
i ++;
}
if (! cuesheets.len ())
return;
// sort cuesheet list in natural order
cuesheets.sort (str_compare_encoded);
// sort file list in system-dependent order for duplicate removal
files.sort (filename_compare);
for (String & cuesheet : cuesheets)
{
AUDINFO ("Adding cuesheet: %s\n", (const char *) cuesheet);
status_update (cuesheet, result->items.len ());
String title; // ignored
Index<PlaylistAddItem> items;
if (! playlist_load (cuesheet, title, items))
continue;
String prev_filename;
for (auto & item : items)
{
String filename = item.tuple.get_str (Tuple::AudioFile);
if (! filename)
continue; // shouldn't happen
if (! filter || filter (item.filename, user))
add_file (std::move (item), filter, user, result, false);
else
result->filtered = true;
// remove duplicates from file list
if (prev_filename && ! filename_compare (filename, prev_filename))
continue;
int idx = files.bsearch ((const char *) filename, filename_compare);
if (idx >= 0)
files.remove (idx, 1);
prev_filename = std::move (filename);
}
}
}
static void add_folder (const char * filename, Playlist::FilterFunc filter,
void * user, AddResult * result, bool save_title)
{
AUDINFO ("Adding folder: %s\n", filename);
status_update (filename, result->items.len ());
String error;
Index<String> files = VFSFile::read_folder (filename, error);
if (error)
aud_ui_show_error (str_printf (_("Error reading %s:\n%s"), filename, (const char *) error));
if (! files.len ())
return;
if (save_title)
{
const char * slash = strrchr (filename, '/');
if (slash)
result->title = String (str_decode_percent (slash + 1));
}
add_cuesheets (files, filter, user, result);
// sort file list in natural order (must come after add_cuesheets)
files.sort (str_compare_encoded);
for (const char * file : files)
{
if (filter && ! filter (file, user))
{
result->filtered = true;
continue;
}
String error;
VFSFileTest mode = VFSFile::test_file (file,
VFSFileTest (VFS_IS_REGULAR | VFS_IS_SYMLINK | VFS_IS_DIR), error);
if (error)
AUDERR ("%s: %s\n", file, (const char *) error);
if (mode & VFS_IS_SYMLINK)
continue;
if (mode & VFS_IS_REGULAR)
add_file ({String (file)}, filter, user, result, true);
else if ((mode & VFS_IS_DIR) && aud_get_bool (nullptr, "recurse_folders"))
add_folder (file, filter, user, result, false);
}
}
static void add_generic (PlaylistAddItem && item, Playlist::FilterFunc filter,
void * user, AddResult * result, bool save_title, bool from_playlist)
{
if (! strstr (item.filename, "://"))
{
/* Let's not add random junk to the playlist. */
AUDERR ("Invalid URI: %s\n", (const char *) item.filename);
return;
}
if (filter && ! filter (item.filename, user))
{
result->filtered = true;
return;
}
/* If the item has a valid tuple or known decoder, or it's a subtune, then
assume it's a playable file and skip some checks. */
if (item.tuple.valid () || item.decoder || is_subtune (item.filename))
add_file (std::move (item), filter, user, result, false);
else
{
int tests = 0;
if (! from_playlist)
tests |= VFS_NO_ACCESS;
if (! from_playlist || aud_get_bool (nullptr, "folders_in_playlist"))
tests |= VFS_IS_DIR;
String error;
VFSFileTest mode = VFSFile::test_file (item.filename, (VFSFileTest) tests, error);
if ((mode & VFS_NO_ACCESS))
aud_ui_show_error (str_printf (_("Error reading %s:\n%s"),
(const char *) item.filename, (const char *) error));
else if (mode & VFS_IS_DIR)
{
add_folder (item.filename, filter, user, result, save_title);
result->saw_folder = true;
}
else if ((! from_playlist) && Playlist::filename_is_playlist (item.filename))
add_playlist (item.filename, filter, user, result, save_title);
else
add_file (std::move (item), filter, user, result, false);
}
}
static void start_thread_locked ()
{
if (add_thread_exited)
{
pthread_mutex_unlock (& mutex);
pthread_join (add_thread, nullptr);
pthread_mutex_lock (& mutex);
}
if (! add_thread_started || add_thread_exited)
{
pthread_create (& add_thread, nullptr, add_worker, nullptr);
add_thread_started = true;
add_thread_exited = false;
}
}
static void stop_thread_locked ()
{
if (add_thread_started)
{
pthread_mutex_unlock (& mutex);
pthread_join (add_thread, nullptr);
pthread_mutex_lock (& mutex);
add_thread_started = false;
add_thread_exited = false;
}
}
static void add_finish (void * unused)
{
pthread_mutex_lock (& mutex);
AddResult * result;
while ((result = add_results.head ()))
{
add_results.remove (result);
PlaylistEx playlist;
int count;
if (! result->items.len ())
{
if (result->saw_folder && ! result->filtered)
aud_ui_show_error (_("No files found."));
goto FREE;
}
playlist = result->playlist;
if (! playlist.exists ()) /* playlist deleted */
goto FREE;
if (result->play)
{
if (aud_get_bool (nullptr, "clear_playlist"))
playlist.remove_all_entries ();
else
playlist.queue_remove_all ();
}
count = playlist.n_entries ();
if (result->at < 0 || result->at > count)
result->at = count;
if (result->title && ! count)
{
if (! strcmp (playlist.get_title (), _("New Playlist")))
playlist.set_title (result->title);
}
/* temporarily disable scanning this playlist; the intent is to avoid
scanning until the currently playing entry is known, at which time it
can be scanned more efficiently (album art read in the same pass). */
playlist_enable_scan (false);
playlist.insert_flat_items (result->at, std::move (result->items));
if (result->play)
{
if (! aud_get_bool (0, "shuffle"))
playlist.set_position (result->at);
playlist.start_playback ();
}
playlist_enable_scan (true);
FREE:
delete result;
}
if (add_thread_exited)
{
stop_thread_locked ();
status_done_locked ();
}
pthread_mutex_unlock (& mutex);
hook_call ("playlist add complete", nullptr);
}
static void * add_worker (void * unused)
{
pthread_mutex_lock (& mutex);
AddTask * task;
while ((task = add_tasks.head ()))
{
add_tasks.remove (task);
current_playlist = task->playlist;
pthread_mutex_unlock (& mutex);
playlist_cache_load (task->items);
AddResult * result = new AddResult ();
result->playlist = task->playlist;
result->at = task->at;
result->play = task->play;
bool save_title = (task->items.len () == 1);
for (auto & item : task->items)
add_generic (std::move (item), task->filter, task->user, result, save_title, false);
delete task;
pthread_mutex_lock (& mutex);
current_playlist = Playlist ();
if (! add_results.head ())
queued_add.queue (add_finish, nullptr);
add_results.append (result);
}
add_thread_exited = true;
pthread_mutex_unlock (& mutex);
return nullptr;
}
void adder_cleanup ()
{
pthread_mutex_lock (& mutex);
add_tasks.clear ();
stop_thread_locked ();
status_done_locked ();
add_results.clear ();
queued_add.stop ();
pthread_mutex_unlock (& mutex);
}
EXPORT void Playlist::insert_entry (int at,
const char * filename, Tuple && tuple, bool play) const
{
Index<PlaylistAddItem> items;
items.append (String (filename), std::move (tuple));
insert_items (at, std::move (items), play);
}
EXPORT void Playlist::insert_items (int at,
Index<PlaylistAddItem> && items, bool play) const
{
insert_filtered (at, std::move (items), nullptr, nullptr, play);
}
EXPORT void Playlist::insert_filtered (int at,
Index<PlaylistAddItem> && items, Playlist::FilterFunc filter, void * user,
bool play) const
{
pthread_mutex_lock (& mutex);
AddTask * task = new AddTask ();
task->playlist = * this;
task->at = at;
task->play = play;
task->items = std::move (items);
task->filter = filter;
task->user = user;
add_tasks.append (task);
start_thread_locked ();
pthread_mutex_unlock (& mutex);
}
EXPORT bool Playlist::add_in_progress () const
{
pthread_mutex_lock (& mutex);
for (AddTask * task = add_tasks.head (); task; task = add_tasks.next (task))
{
if (task->playlist == * this)
goto YES;
}
if (current_playlist == * this)
goto YES;
for (AddResult * result = add_results.head (); result; result = add_results.next (result))
{
if (result->playlist == * this)
goto YES;
}
pthread_mutex_unlock (& mutex);
return false;
YES:
pthread_mutex_unlock (& mutex);
return true;
}
EXPORT bool Playlist::add_in_progress_any ()
{
pthread_mutex_lock (& mutex);
bool in_progress = (add_tasks.head () ||
current_playlist != Playlist () ||
add_results.head ());
pthread_mutex_unlock (& mutex);
return in_progress;
}</pre></code>
<p>Para iniciarmos nossa análise vamos falar brevemente sobre a biblioteca utilizada
para o controle de threads a pthread.h, vem da definição de POSIX threads que é um padrão
POSIX para threads, o qual define uma API padrão para criar e manipular threads. As
bibliotecas que implementam a POSIX threads são chamadas Pthreads, sendo muito difundidas
no universo Unix e outros sistemas operacionais semelhantes como Linux e Solaris. </p>

<p>Na sequência foi definida uma variável </p>

<pre><code class="c++">static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER,


</pre></code>

<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>

<pre><code class="c++">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);
}</pre></code>

<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>

<pre><code class="c++">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);
}</pre></code>

<p>É de grande importância observar que as funções acima, status_cb() e


status_update() contém em seu corpo as chamadas de funções pthread_mutex_lock() e
pthread_mutex_unlock() que recebem como parâmetros o objeto referênciado pelo mutex a
sere bloqueada. Quando chamada a função pthread_mutex_lock() é chamada se o objeto
mutex já estiver bloqueado o segmento de chamada bloqueia até que o mutex se torne
disponível, o que se dará depois do tempo de bloqueio definido na passagem de parâmetros
para as funções as função pthread_mutex_unlock() de maneira análoga fará o desbloqueio do
objeto mutex. </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>

<pre><code class="c++">static bool add_thread_started = false;


static bool add_thread_exited = false;
static pthread_t add_thread; </pre></code>
<p>Essas variáveis serão utilizadas nos procedimentos de controle na inicialização e
parada das threads que seguem abaixo. </p>

<pre><code class="c++">static void start_thread_locked ()


{
if (add_thread_exited)
{
pthread_mutex_unlock (& mutex);
pthread_join (add_thread, nullptr);
pthread_mutex_lock (& mutex);
}
if (! add_thread_started || add_thread_exited)
{
pthread_create (& add_thread, nullptr, add_worker, nullptr);
add_thread_started = true;
add_thread_exited = false;
}
}
static void stop_thread_locked ()
{
if (add_thread_started)
{
pthread_mutex_unlock (& mutex);
pthread_join (add_thread, nullptr);
pthread_mutex_lock (& mutex);
add_thread_started = false;
add_thread_exited = false;
}
}</pre></code>
<p>Como visto acima de acordo com os valores dos status das funções de bloqueio e
desbloqueio, mas também faz uso da função pthread_join() que suspende a execução do
encadeamento de chamada até que o encadeamento de destino seja finalizado, a menos que o
encadeamsnto de destino já tenha terminado, se bem sucedida, a função pthread_join()
retorna zero, caso contrário um número de erro é retornado para indicar o erro. E temos
também as variáveis booleanas já mencionadas que definem o inicio e a exitação da thread
alternando seus valores passados entre true ou false.

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>

<h3>Análise do código cue-cache.cc</h3>

<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.

A seguir a sequência de código, </p>


<pre><code class="c++">/*
* cue-cache.cc
* Copyright 2016 John Lindgren
*/

#include "cue-cache.h"
#include "multihash.h"
#include "playlist-internal.h"

#include <pthread.h>

enum NodeState {NotLoaded, Loading, Loaded};

struct CueCacheNode {
Index<PlaylistAddItem> items;
NodeState state = NotLoaded;
int refcount = 0;
};

static SimpleHash<String, CueCacheNode> cache;


static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

CueCacheRef::CueCacheRef (const char * filename) :


m_filename (filename)
{
pthread_mutex_lock (& mutex);

m_node = cache.lookup (m_filename);


if (! m_node)
m_node = cache.add (m_filename, CueCacheNode ());

m_node->refcount ++;

pthread_mutex_unlock (& mutex);


}

CueCacheRef::~CueCacheRef ()
{
pthread_mutex_lock (& mutex);

m_node->refcount --;
if (! m_node->refcount)
cache.remove (m_filename);

pthread_mutex_unlock (& mutex);


}
const Index<PlaylistAddItem> & CueCacheRef::load ()
{
String title; // not used
pthread_mutex_lock (& mutex);

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;
}

pthread_mutex_unlock (& mutex);


return m_node->items;
}</pre></code>

<p>Nesta sequência de código tem como mecanismos de controle a variável mutex do


tipo pthread_mutex_t para referenciar o objeto mutex e recebe o valor passado por
PTHREAD_MUTEX_INITIALIZER externa ao código já definida no código de origem já incluido.
Também é definida a variável cond do tipo pthread_cond_t que recebe como valor passado
por PTHREAD_COND_INITIALIZER.

O código faz uso da pthread_cond_broadcast função () que deve desbloquear os


tópicos atualmente bloqueados na variável condição especificada em cond. Se bem-sucedida,
a função pthread_cond_broadcast () deve retornar zero; caso contrário, um número de erro
deve ser retornado para indicar o erro.

A função pthread_cond_wait () sã ousadas para bloquear uma variável de condição.


Eles sã ochamados com o mutex bloqueado pelo encadeamento de chamada ou resultará um
comportamento indefinido.
Essa função libera atomicamente o mutex e faz com que o encadeamento de chamada
bloqueie na variável de condição cond ; atomicamente que significa "atomicamente em
relação ao acesso por outro segmento ao mutex e depois à variável de condição".

Exceto no caso de [ETIMEDOUT], todas essas verificações de erro agem como se


fossem executadas imediatamente no início do processamento da função e causam um
retorno de erro, com efeito, antes de modificar o estado do mutex especificado por mutex ou
pelo variável de condição especificada por cond .

Após a conclusão bem-sucedida, um valor de zero é retornado. Caso contrário, um número de


erro é retornado para indicar o erro. </p>

<h3>Análise do código mainloop.h</h3>

<p>A seguir estará sendo analisando a sequência de código mainloop.h comentando


algumas partes importantes o que ajudará no entendimento do código, porém fica a cargo do
leitor absorver os conteúdos extras, que não serão abordados neste tópico, neste caso vamos
nos ater aos mecanismos que controlam os processos. </p>

<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>

<pre><code class="c++">#ifndef LIBAUDCORE_MAINLOOP_H


#define LIBAUDCORE_MAINLOOP_H
class QueuedFunc
{
public:
typedef void (* Func) (void * data);
// one-time idle callback
void queue (Func func, void * data);
// one-time delayed callback
void queue (int delay_ms, Func func, void * data);
// periodic timer callback
void start (int interval_ms, Func func, void * data);
// stops any type of callback
// note that queue() and start() also stop any previous callback
void stop ();
// true if a periodic timer is running
// does not apply to one-time callbacks
bool running () const
{
return _running;
}
constexpr QueuedFunc () = default;
QueuedFunc (const QueuedFunc &) = delete;
void operator= (const QueuedFunc &) = delete;
~QueuedFunc ()
{
stop ();
}
// cancels any pending callbacks
// inhibits all future callbacks
// needed to allow safe shutdown of some (Qt!) main loops
static void inhibit_all ();
private:
bool _running = false;
};
void mainloop_run ();
void mainloop_quit ();
#endif</pre></code>

<p>A variável externa LIBAUDCORE_MAINLOOP_H é uma definição e para isto utiliza


as #ifndef e #define para sua utilização e das funções e procedimentos que a compõe.

A Diretiva ifndef A diretiva #ifndef funciona ao contrário da diretiva #ifdef. Sua forma
geral é: </p>

<pre><code class="c++">#ifndef nome_da_macro


sequência_de_declarações
#endif</pre></code>

<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>

<pre><code class="c++">typedef antigo_nome novo_nome; </pre></code>


<p>Como exemplo vamos dar o nome de inteiro para o tipo int: </p>

<pre><code class="c++">typedef int inteiro; </pre></code>


<p>Agora podemos declarar o tipo inteiro.

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.

A função chamada ~QueuedFunc() faz o cancelamento de todas as chamadas de


retorno pendentes, também inibe todos os futuros retornos de chamadas, fazendo assim o
desligamento seguro de aluns loops principais(Qt!). </p>

<h3>Análise do código playback.cc</h3>


<p>Desde o Audacious 3.6, o encadeamento de playback é completamente assíncrono;
isso é, a thread principal nunca bloqueia a espera para o segmento de reprodução para
processar um comando play (), seek () ou stop (). Como resultado, o encadeamento de
reprodução pode atrasar por trás do tópico principal / playlist, e a música "atual" da
perspectiva do segmento de reprodução pode não ser o mesmo que a música "atual" da
perspectiva do segmento principal / playlist. Portanto, alguns cuidados são necessários para
garantir que as informações geradas no encadeamento de reprodução não sejam aplicadas à
música errada. Para este fim, números de série separados são mantidos por cada thread e
comparados quando a informação ultrapassa os limites das threads; se os números séries não
coincidem, a informação é geralmente descartada.

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

POSIX.1 FAQ. The Open Group. October 5, 2011

http://www.uniriotec.br/~morganna/guia/libc/fc_fflush.html

http://www.br-c.org/doku.php?id=snprintf15:12 22/05/2018</p>

Вам также может понравиться