Skip to content
This repository has been archived by the owner on Nov 10, 2021. It is now read-only.

youtube-dl integration & more intervals #8

Merged
merged 1 commit into from
Dec 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ project(VidifyAudiosync
LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

# Debug configuration
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -g3 -std=c11 \
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -g3 -std=c99 \
-DDEBUG -fsanitize=undefined -fsanitize=address")
# Release configuration
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wall -Wextra -O3")
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,23 @@ The only exported method is `get_lag(url: str)`, which returns the displacement

There's a simple example implementation in the [example.py](https://github.com/marioortizmanero/vidify-audiosync/blob/master/examples/example.py) file.

There are 2 apps to try:

* `apps/main.c`: used to debug more easily. You can run it with:

```shell
mkdir build
cd build
mkdir images
cmake ..
make
./apps/main "SONG NAME"
```

Use `-DCMAKE_BUILD_TYPE=Debug` to enable debugging and save plots into the images directory. You'll need `gnuplot` installed for that.

* `apps/main.py`: the actual module usage. You can simply use `python main.py "SONG NAME"`


## How it works
*I'll try to explain it as clearly as possible, since this took me a lot of effort to understand without prior knowledge about the mathematics behind it. If someone with a better understanding of the calculations performed in this module considers that the explanation could be improved, please [create an issue](https://github.com/marioortizmanero/vidify-audiosync/issues) to let me know.*
Expand Down
2 changes: 1 addition & 1 deletion apps/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ add_executable(
${HEADER_LIST}
)

target_compile_features(main PRIVATE c_std_11)
target_compile_features(main PRIVATE c_std_99)

target_link_libraries(main PRIVATE vidify_audiosync fftw3 m pthread)
5 changes: 5 additions & 0 deletions apps/images/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Ignore everything in this directory
*.png

# Except this file
!.gitignore
3 changes: 2 additions & 1 deletion apps/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@

int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s URL\n", argv[0]);
printf("Usage: %s \"TITLE\"\n", argv[0]);
exit(1);
}

printf("Running get_lag\n");
int ret = get_lag(argv[1]);
printf("Returned value: %d\n", ret);

Expand Down
10 changes: 1 addition & 9 deletions apps/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,7 @@
" background.")
exit()

# Obtaining an URL from a song name with youtube_dl
ytdl_opts = {
'format': 'bestaudio'
}
with youtube_dl.YoutubeDL(ytdl_opts) as ytdl:
info = ytdl.extract_info(f"ytsearch:{sys.argv[1]}", download=False)
url = info['entries'][0]['url']

# After this is printed, the music should start playing in the background too
print("Running audiosync.get_lag")
ret = audiosync.get_lag(url)
ret = audiosync.get_lag(sys.argv[1])
print(f"Returned value: {ret}")
Binary file modified images/144000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/144000_original.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/288000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/288000_original.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/480000.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/480000_original.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions include/vidify_audiosync/download/linux_download.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@

void *download(void *);

int get_audio_url(char *title, char **url);


#endif /* _H_DOWNLOAD_ */
8 changes: 5 additions & 3 deletions include/vidify_audiosync/global.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
// milliseconds.
#define FRAMES_TO_MS (1000.0 / (double) SAMPLE_RATE)

// The value of the last interval in audiosync.c in seconds.
#define MAX_SECONDS_STR "15"
// The value of the last interval in audiosync.c in seconds. This avoids
// having to run an itoa() for intervals[n_intervals-1] and simplifies it a
// bit.
#define MAX_SECONDS_STR "30"

// The minimum cross-correlation coefficient accepted.
#define MIN_CONFIDENCE 0.75
Expand All @@ -32,7 +34,7 @@ struct thread_data {
int *end;
};
struct down_data {
char *url;
char *yt_title;
struct thread_data *th_data;
};

Expand Down
4 changes: 2 additions & 2 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ add_library(

target_include_directories(vidify_audiosync PUBLIC ../include)

# C11 is required
target_compile_features(vidify_audiosync PUBLIC c_std_11)
# C99 is required
target_compile_features(vidify_audiosync PUBLIC c_std_99)

# For the IDEs
source_group(TREE "${PROJECT_SOURCE_DIR}/include" PREFIX "Header Files" FILES ${HEADERS})
11 changes: 6 additions & 5 deletions src/audiosync.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "../include/vidify_audiosync/download/linux_download.h"


int get_lag(char *url) {
int get_lag(char *yt_title) {
// The audio data.
double *arr1, *arr2;
// Variable used to indicate the other threads to end. Any value other
Expand All @@ -37,9 +37,10 @@ int get_lag(char *url) {
const size_t intervals[] = {
3 * SAMPLE_RATE, // 144000 frames
6 * SAMPLE_RATE, // 288000 frames
9 * SAMPLE_RATE, // 432000 frames
12 * SAMPLE_RATE, // 576000 frames
15 * SAMPLE_RATE, // 720000 frames
10 * SAMPLE_RATE, // 432000 frames
15 * SAMPLE_RATE, // 576000 frames
20 * SAMPLE_RATE, // 720000 frames
30 * SAMPLE_RATE, // 720000 frames
};
const size_t n_intervals = sizeof(intervals) / sizeof(intervals[0]);
const size_t length = intervals[n_intervals-1];
Expand Down Expand Up @@ -83,7 +84,7 @@ int get_lag(char *url) {
// Data structure passed to the download thread. It's a different type
// because it also needs the url of the video to download.
struct down_data down_th_params = {
.url = url,
.yt_title = yt_title,
.th_data = &down_params
};
if (pthread_create(&cap_th, NULL, &capture, (void *) &cap_params) < 0) {
Expand Down
6 changes: 3 additions & 3 deletions src/bind.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ PyMODINIT_FUNC PyInit_vidify_audiosync(void) {
}

PyObject *audiosync_get_lag(PyObject *self, PyObject *args) {
char *url;
if (!PyArg_ParseTuple(args, "s", &url)) {
char *yt_title;
if (!PyArg_ParseTuple(args, "s", &yt_title)) {
return NULL;
}

int ret = get_lag(url);
int ret = get_lag(yt_title);
return PyLong_FromLong(ret);
}
1 change: 1 addition & 0 deletions src/capture/linux_capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <pthread.h>
#include "../../include/vidify_audiosync/global.h"
#include "../../include/vidify_audiosync/ffmpeg_pipe.h"
#include "../../include/vidify_audiosync/capture/linux_capture.h"


// NOTE: for now requires to change the captured sink in pavucontrol to
Expand Down
17 changes: 10 additions & 7 deletions src/cross_correlation.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include "../include/vidify_audiosync/global.h"



// Data structure used to pass parameters to concurrent FFTW-related functions.
struct fftw_data {
double *real;
Expand Down Expand Up @@ -54,6 +53,10 @@ static void *fft(void *thread_arg) {
// calculate the circular cross-correlation rather than the regular
// cross-correlation.
//
// Note: if debugging mode is enabled, plots of each run will be saved in an
// images directory. The titles assume that `input1` is the captured data,
// and `input2` is the downloaded data.
//
// Returns the lag in frames the second data set has over the first one, with
// a confidence between -1 and 1.
//
Expand Down Expand Up @@ -83,13 +86,13 @@ int cross_correlation(double *input1, double *input2, const size_t input_length,
FILE *gnuplot = popen("gnuplot", "w");
fprintf(gnuplot, "set term 'png'\n");
fprintf(gnuplot, "set output 'images/%ld_original.png'\n", input_length);
fprintf(gnuplot, "plot '-' with lines title 'data1', '-' with lines title 'data2'\n");
fprintf(gnuplot, "plot '-' with lines title 'downloaded', '-' with lines title 'captured'\n");
for (size_t i = 0; i < input_length; ++i)
fprintf(gnuplot, "%f\n", data1[i]);
fprintf(gnuplot, "%f\n", data2[i]);
fprintf(gnuplot, "e\n");
// The second audio file starts at samplesDelay
for (size_t i = 0; i < input_length; ++i)
fprintf(gnuplot, "%f\n", data2[i]);
fprintf(gnuplot, "%f\n", data1[i]);
fprintf(gnuplot, "e\n");
fflush(gnuplot);
pclose(gnuplot);
Expand Down Expand Up @@ -207,13 +210,13 @@ int cross_correlation(double *input1, double *input2, const size_t input_length,
gnuplot = popen("gnuplot", "w");
fprintf(gnuplot, "set term 'png'\n");
fprintf(gnuplot, "set output 'images/%ld.png'\n", input_length);
fprintf(gnuplot, "plot '-' with lines title 'data1', '-' with lines title 'data2'\n");
fprintf(gnuplot, "plot '-' with lines title 'downloaded', '-' with lines title 'captured'\n");
for (size_t i = 0; i < input_length; ++i)
fprintf(gnuplot, "%f\n", data1[i]);
fprintf(gnuplot, "%f\n", data2[i]);
fprintf(gnuplot, "e\n");
// The second audio file starts at samplesDelay
for (size_t i = 0; i < input_length; ++i)
fprintf(gnuplot, "%f\n", data2[i]);
fprintf(gnuplot, "%f\n", data1[i]);
fprintf(gnuplot, "e\n");
fflush(gnuplot);
pclose(gnuplot);
Expand Down
52 changes: 49 additions & 3 deletions src/download/linux_download.c
Original file line number Diff line number Diff line change
@@ -1,20 +1,66 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include "../../include/vidify_audiosync/global.h"
#include "../../include/vidify_audiosync/ffmpeg_pipe.h"
#include "../../include/vidify_audiosync/download/linux_download.h"

#define MAX_LONG_URL 4086
#define MAX_LONG_COMMAND 4086


void *download(void *arg) {
struct down_data *data;
data = (struct down_data *) arg;

char *url = malloc(sizeof(char) * MAX_LONG_URL);
if (url == NULL) {
perror("malloc");
goto finish;
}

if (get_audio_url(data->yt_title, &url) < 0) {
fprintf(stderr, "Could not obtain youtube url.\n");
goto finish;
}

char *args[] = {
"ffmpeg", "-y", "-to", "15", "-i", data->url, "-ac",
NUM_CHANNELS_STR, "-r", SAMPLE_RATE_STR, "-f", "f64le", "pipe:1",
NULL
"ffmpeg", "-y", "-to", "15", "-i", url, "-ac", NUM_CHANNELS_STR, "-r",
SAMPLE_RATE_STR, "-f", "f64le", "pipe:1", NULL
};
read_pipe(data->th_data, args);

finish:
if (url) free(url);
pthread_exit(NULL);
}


// Obtains the audio direct link with Youtube-dl.
int get_audio_url(char *title, char **url) {
int ret = -1;

// Creating the full command
char command[MAX_LONG_COMMAND];
strcpy(command, "youtube-dl -g -f bestaudio ytsearch:\"");
strcat(command, title);
strcat(command, "\"");

// Run the command and read the output
FILE *fp = popen(command, "r");
if (fp == NULL) {
fprintf(stderr, "Failed to run command\n" );
goto finish;
}
printf("Getting url 1 %s\n", command);
fscanf(fp, "%s", *url);
printf("Getting url 2\n");
pclose(fp);

ret = 0;

finish:
return ret;
}
4 changes: 2 additions & 2 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ add_executable(
${HEADERS}
)

# Minimum C11 required. Also listing the used libraries.
target_compile_features(tests PRIVATE c_std_11)
# Minimum C99 required. Also listing the used libraries.
target_compile_features(tests PRIVATE c_std_99)
target_link_libraries(tests PRIVATE vidify_audiosync fftw3 m pthread)

# Adding the tests one by one for the make command:
Expand Down