Skip to content

Commit

Permalink
added missing docstrings (#544)
Browse files Browse the repository at this point in the history
  • Loading branch information
Josef-Haupt authored Jan 15, 2025
1 parent 9e824e4 commit 96902d8
Show file tree
Hide file tree
Showing 10 changed files with 413 additions and 45 deletions.
157 changes: 140 additions & 17 deletions birdnet_analyzer/analyze.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,19 @@ def loadCodes():
return codes


def generate_raven_table(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str) -> str:
def generate_raven_table(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str):
"""
Generates a Raven selection table from the given timestamps and prediction results.
Args:
timestamps (list[str]): List of timestamp strings in the format "start-end".
result (dict[str, list]): Dictionary where keys are timestamp strings and values are lists of predictions.
afile_path (str): Path to the audio file being analyzed.
result_path (str): Path where the resulting Raven selection table will be saved.
Returns:
None
"""
selection_id = 0
out_string = RAVEN_TABLE_HEADER

Expand Down Expand Up @@ -104,7 +116,19 @@ def generate_raven_table(timestamps: list[str], result: dict[str, list], afile_p
utils.save_result_file(result_path, out_string)


def generate_audacity(timestamps: list[str], result: dict[str, list], result_path: str) -> str:
def generate_audacity(timestamps: list[str], result: dict[str, list], result_path: str):
"""
Generates an Audacity timeline label file from the given timestamps and results.
Args:
timestamps (list[str]): A list of timestamp strings.
result (dict[str, list]): A dictionary where keys are timestamps and values are lists of tuples,
each containing a label and a confidence score.
result_path (str): The file path where the result string will be saved.
Returns:
None
"""
out_string = ""

# Audacity timeline labels
Expand All @@ -124,7 +148,20 @@ def generate_audacity(timestamps: list[str], result: dict[str, list], result_pat
utils.save_result_file(result_path, out_string)


def generate_rtable(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str) -> str:
def generate_rtable(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str):
"""
Generates a R table string from the given timestamps and result data, and saves it to a file.
Args:
timestamps (list[str]): A list of timestamp strings in the format "start-end".
result (dict[str, list]): A dictionary where keys are timestamp strings and values are lists of tuples containing
classification results (label, confidence).
afile_path (str): The path to the audio file being analyzed.
result_path (str): The path where the result table file will be saved.
Returns:
None
"""
out_string = RTABLE_HEADER

for timestamp in timestamps:
Expand Down Expand Up @@ -157,7 +194,20 @@ def generate_rtable(timestamps: list[str], result: dict[str, list], afile_path:
utils.save_result_file(result_path, out_string)


def generate_kaleidoscope(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str) -> str:
def generate_kaleidoscope(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str):
"""
Generates a Kaleidoscope-compatible CSV string from the given timestamps and results, and saves it to a file.
Args:
timestamps (list[str]): List of timestamp strings in the format "start-end".
result (dict[str, list]): Dictionary where keys are timestamp strings and values are lists of tuples containing
species label and confidence score.
afile_path (str): Path to the audio file being analyzed.
result_path (str): Path where the resulting CSV file will be saved.
Returns:
None
"""
out_string = KALEIDOSCOPE_HEADER

folder_path, filename = os.path.split(afile_path)
Expand Down Expand Up @@ -192,7 +242,20 @@ def generate_kaleidoscope(timestamps: list[str], result: dict[str, list], afile_
utils.save_result_file(result_path, out_string)


def generate_csv(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str) -> str:
def generate_csv(timestamps: list[str], result: dict[str, list], afile_path: str, result_path: str):
"""
Generates a CSV file from the given timestamps and results.
Args:
timestamps (list[str]): A list of timestamp strings in the format "start-end".
result (dict[str, list]): A dictionary where keys are timestamp strings and values are lists of tuples.
Each tuple contains a label and a confidence score.
afile_path (str): The file path of the audio file being analyzed.
result_path (str): The file path where the resulting CSV file will be saved.
Returns:
None
"""
out_string = CSV_HEADER

for timestamp in timestamps:
Expand All @@ -212,12 +275,16 @@ def generate_csv(timestamps: list[str], result: dict[str, list], afile_path: str


def saveResultFiles(r: dict[str, list], result_files: dict[str, str], afile_path: str):
"""Saves the results to the hard drive.
"""
Saves the result files in various formats based on the provided configuration.
Args:
r: The dictionary with {segment: scores}.
path: The path where the result should be saved.
afile_path: The path to audio file.
r (dict[str, list]): A dictionary containing the analysis results with timestamps as keys.
result_files (dict[str, str]): A dictionary mapping result types to their respective file paths.
afile_path (str): The path to the audio file being analyzed.
Returns:
None
"""

os.makedirs(cfg.OUTPUT_PATH, exist_ok=True)
Expand All @@ -242,6 +309,15 @@ def saveResultFiles(r: dict[str, list], result_files: dict[str, str], afile_path


def combine_raven_tables(saved_results: list[str]):
"""
Combines multiple Raven selection table files into a single file and adjusts the selection IDs and times.
Args:
saved_results (list[str]): List of file paths to the Raven selection table files to be combined.
Returns:
None
"""
# Combine all files
s_id = 1
time_offset = 0
Expand Down Expand Up @@ -303,6 +379,15 @@ def combine_raven_tables(saved_results: list[str]):


def combine_rtable_files(saved_results: list[str]):
"""
Combines multiple R table files into a single file.
Args:
saved_results (list[str]): A list of file paths to the result table files to be combined.
Returns:
None
"""
# Combine all files
with open(os.path.join(cfg.OUTPUT_PATH, cfg.OUTPUT_RTABLE_FILENAME), "w", encoding="utf-8") as f:
f.write(RTABLE_HEADER)
Expand All @@ -326,6 +411,15 @@ def combine_rtable_files(saved_results: list[str]):


def combine_kaleidoscope_files(saved_results: list[str]):
"""
Combines multiple Kaleidoscope result files into a single file.
Args:
saved_results (list[str]): A list of file paths to the saved Kaleidoscope result files.
Returns:
None
"""
# Combine all files
with open(os.path.join(cfg.OUTPUT_PATH, cfg.OUTPUT_KALEIDOSCOPE_FILENAME), "w", encoding="utf-8") as f:
f.write(KALEIDOSCOPE_HEADER)
Expand All @@ -349,6 +443,12 @@ def combine_kaleidoscope_files(saved_results: list[str]):


def combine_csv_files(saved_results: list[str]):
"""
Combines multiple CSV files into a single CSV file.
Args:
saved_results (list[str]): A list of file paths to the CSV files to be combined.
"""
# Combine all files
with open(os.path.join(cfg.OUTPUT_PATH, cfg.OUTPUT_CSV_FILENAME), "w", encoding="utf-8") as f:
f.write(CSV_HEADER)
Expand All @@ -372,6 +472,19 @@ def combine_csv_files(saved_results: list[str]):


def combineResults(saved_results: list[dict[str, str]]):
"""
Combines various types of result files based on the configuration settings.
This function checks the types of results specified in the configuration
and combines the corresponding files from the saved results list.
Args:
saved_results (list[dict[str, str]]): A list of dictionaries containing
file paths for different result types. Each dictionary represents
a set of result files for a particular analysis.
Returns:
None
"""
if "table" in cfg.RESULT_TYPES:
combine_raven_tables([f["table"] for f in saved_results if f])

Expand All @@ -398,9 +511,7 @@ def getSortedTimestamps(results: dict[str, list]):


def getRawAudioFromFile(fpath: str, offset, duration):
"""Reads an audio file.
Reads the file and splits the signal into chunks.
"""Reads an audio file and splits the signal into chunks.
Args:
fpath: Path to the audio file.
Expand Down Expand Up @@ -438,6 +549,16 @@ def predict(samples):


def get_result_file_names(fpath: str):
"""
Generates a dictionary of result file names based on the input file path and configured result types.
Args:
fpath (str): The file path of the input file.
Returns:
dict: A dictionary where the keys are result types (e.g., "table", "audacity", "r", "kaleidoscope", "csv")
and the values are the corresponding output file paths.
"""
result_names = {}

rpath = fpath.replace(cfg.INPUT_PATH, "")
Expand Down Expand Up @@ -466,15 +587,17 @@ def get_result_file_names(fpath: str):


def analyzeFile(item):
"""Analyzes a file.
Predicts the scores for the file and saves the results.
"""
Analyzes an audio file and generates prediction results.
Args:
item: Tuple containing (file path, config)
item (tuple): A tuple containing the file path (str) and configuration settings.
Returns:
The `True` if the file was analyzed successfully.
dict or None: A dictionary of result file names if analysis is successful,
None if the file is skipped or an error occurs.
Raises:
Exception: If there is an error in reading the audio file or saving the results.
"""
# Get file path and restore cfg
fpath: str = item[0]
Expand Down
54 changes: 51 additions & 3 deletions birdnet_analyzer/audio.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,31 @@ def openAudioFile(path: str, sample_rate=48000, offset=0.0, duration=None, fmin=


def getAudioFileLength(path, sample_rate=48000):
"""
Get the length of an audio file in seconds.
Args:
path (str): The file path to the audio file.
sample_rate (int, optional): The sample rate to use for reading the audio file. Default is 48000.
Returns:
float: The duration of the audio file in seconds.
"""
# Open file with librosa (uses ffmpeg or libav)

return librosa.get_duration(filename=path, sr=sample_rate)


def get_sample_rate(path: str):
"""
Get the sample rate of an audio file.
Args:
path (str): The file path to the audio file.
Returns:
int: The sample rate of the audio file.
"""
return librosa.get_samplerate(path)


Expand All @@ -52,15 +71,16 @@ def saveSignal(sig, fname: str):
Args:
sig: The signal to be saved.
fname: The file path.
Returns:
None
"""

sf.write(fname, sig, 48000, "PCM_16")


def pad(sig, seconds, srate, amount=None):
"""Creates noise.
Creates a noise vector with the given shape.
"""Creates a noise vector with the given shape.
Args:
sig: The original audio signal.
Expand Down Expand Up @@ -165,6 +185,9 @@ def cropCenter(sig, rate, seconds):
sig: The original signal.
rate: The sampling rate.
seconds: The length of the signal.
Returns:
The cropped signal.
"""
if len(sig) > int(seconds * rate):
start = int((len(sig) - int(seconds * rate)) / 2)
Expand All @@ -179,6 +202,19 @@ def cropCenter(sig, rate, seconds):


def bandpass(sig, rate, fmin, fmax, order=5):
"""
Apply a bandpass filter to the input signal.
Args:
sig (numpy.ndarray): The input signal to be filtered.
rate (int): The sampling rate of the input signal.
fmin (float): The minimum frequency for the bandpass filter.
fmax (float): The maximum frequency for the bandpass filter.
order (int, optional): The order of the filter. Default is 5.
Returns:
numpy.ndarray: The filtered signal as a float32 array.
"""
# Check if we have to bandpass at all
if fmin == cfg.SIG_FMIN and fmax == cfg.SIG_FMAX or fmin > fmax:
return sig
Expand Down Expand Up @@ -216,6 +252,18 @@ def bandpass(sig, rate, fmin, fmax, order=5):
# For a complete description of this method, see Discrete-Time Signal Processing
# (Second Edition), by Alan Oppenheim, Ronald Schafer, and John Buck, Prentice Hall 1998, pp. 474-476.
def bandpassKaiserFIR(sig, rate, fmin, fmax, width=0.02, stopband_attenuation_db=100):
"""
Applies a bandpass filter to the given signal using a Kaiser window FIR filter.
Args:
sig (numpy.ndarray): The input signal to be filtered.
rate (int): The sample rate of the input signal.
fmin (float): The minimum frequency of the bandpass filter.
fmax (float): The maximum frequency of the bandpass filter.
width (float, optional): The transition width of the filter. Default is 0.02.
stopband_attenuation_db (float, optional): The desired attenuation in the stopband, in decibels. Default is 100.
Returns:
numpy.ndarray: The filtered signal as a float32 numpy array.
"""
# Check if we have to bandpass at all
if fmin == cfg.SIG_FMIN and fmax == cfg.SIG_FMAX or fmin > fmax:
return sig
Expand Down
6 changes: 6 additions & 0 deletions birdnet_analyzer/embeddings.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@


def writeErrorLog(msg):
"""
Appends an error message to the error log file.
Args:
msg (str): The error message to be logged.
"""
with open(cfg.ERROR_LOG_FILE, "a") as elog:
elog.write(msg + "\n")

Expand Down
12 changes: 12 additions & 0 deletions birdnet_analyzer/gui/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@


def analyzeFile_wrapper(entry):
"""
Wrapper function for analyzing a file.
Args:
entry (tuple): A tuple where the first element is the file path and the
remaining elements are arguments to be passed to the
analyze.analyzeFile function.
Returns:
tuple: A tuple where the first element is the file path and the second
element is the result of the analyze.analyzeFile function.
"""
return (entry[0], analyze.analyzeFile(entry))


Expand Down
Loading

0 comments on commit 96902d8

Please sign in to comment.