How To
This section provides you with the tools that will help you take full advantage of this library
All the examples presented in this page can be copied and tested in a file (or in a Jupyter Notebook) containing the following basic instructions:
Code
from freesound import *
API_KEY = "<your-api-key>"
USER_ID = "<your-user-id>"
c = FreeSoundClient(USER_ID,API_KEY)
INFO you can find the source code of the code showed in these guides on GitHub
Filter by Audio Feature
The filters
parameter of the FreeSoundClient.search()
method allows you to request information about sound files that only holds certain values for specific fields
, i.e. it allows you to filter out those files which do not comply with specific attributes.
The Freesound API includes filters
based on the [Audio Commons Initiative][http://www.audiocommons.org/] project, which aimed at promoting the use of open audio content and at developing technologies with which to support an envisioned ecosystem of content repositories, production tools and users.
A list of all the Audio Common filters can be found in the Appendix Lists of this Documentation These filters are all prefixed with the label
ac_
and can be used as regular filters, i.e. by specifying them in the search'sfilter
attribute either with a string or with theFreeSoundFilter
utility.
Ranges and Conditions in Filters
Filters can be a double-edge sword when you want to narrow down your searches. Let's consider a simple scenario:
Code
filters = FreeSoundFilters(duration=1).aslist
result = c.search("piano",fields=Field.duration,filter=filters)
This query will filter out those sound whose duration is 1 second, or rather EXACTLY 1 second, no more, no less.
Still, by the time of writing this article, this query will produce 51 results.
Because of this the Freesound API allows filtering by range, a feature that this library implements with the Filter
class and its 3 classmethods
:
Filter.RANGE(minimum,maximum)
to add filters in a range between 2 values ()Filter.AT_LEAST(minimum)
to specify only a minimum limit and can be interpreted asgreater than
Filter.UP_TO(maximum)
to specify only a maximum limit and can be interpreted aslower than
Substituting a range to the previous example means:
Code
filters = FreeSoundFilters(duration=Filter.RANGE(0.8, 1.2)).aslist
result = c.search("piano",fields=Field.duration,filter=filters)
Your search will include those files which are approximately 1 second long, giving you a wider choice of sounds.
For filters such as created
it might be especially useful the use of ranges of time frames, and the best way to format a time range is the Python built in's library datetime
. The class Filter
will automatically understand that you are using a datetime
object, and it will automatically format the date in isoformat
for you.
If you are not familiar with the
datetime
library, this article might help.
Code
filters = FreeSoundFilters(created=Filter.RANGE((datetime.now()-timedelta(weeks=20)),datetime.now())).aslist
result = c.search("music",fields=Field.duration,filter=filters)
This query will filter out only those files which are created in the last 20 weeks.
The Filter
class also provides 2 conditionals statements:
Filter.OR
Filter.AND
This feature allows you to build queries such as:
Code
filters = FreeSoundFilters(type=Filter.OR('wav','aiff'), duration=Filter.UP_TO(1),created=Filter.RANGE((datetime.now()-timedelta(weeks=20)),datetime.now())).aslist
which will exclude mp3
files, ogg
files etc. from the search results.
Sorting (FreeSoundSort
)
Another utility that can help you formulate your queries is the FreeSoundSort
class.
The sort
attribute of the FreeSoundClient.search()
method aspects 9 possible values:
- score
- duration_desc
- duration_asc
- created_desc
- created_asc
- downloads_desc
- downloads_asc
- rating_desc
- rating_asc
if you do forget these values you can always start typing FreeSoundSort.
and your IDE will suggest you possible valid values that you can use in your queries:

This example shows how to use it in context:
Code
c.search(query="piano", sort_by=FreeSoundSort.created_asc)
Download a lot of files at once
Another important parameter of the FreesuondClient.search()
method is page_size
which by default is set to 15 and states the max number of items that will be downloaded in one page
of the response. When you need to gather a lot of data from the Freesound Database you can hence reduce the number of requests by using a higher page_size
value (up to 150).
This parameter works well in coordination with FreeSoundClient.download_results()
a method that you might have already seen in the Tutorial
. This method allows you to download sound files from a query response and deals with pagination automatically.
It accepts 2 parameters:
output_folder_path
, which defines the folder in which the files will be downloadedfiles_count
the number of files that you want to download
Code
c.download_results(output_folder_path="sound_lib", files_count=100)
So if you want to download 100 files from the Freesound Database the best approach would be to set both search(...,page_size=100)
and download_results(files_count=100)
:
Code
filters = FreeSoundFilters(type="wav", samplerate=48000).aslist
c.search(query="piano", fields=Field.download, filter=filters, page_size=100)
c.download_results(output_folder="tutorials/sound_lib",files_count=100)
With this approach you will limit the requests to the minimum because the search results are not paginated and download_results
will find all the download links in one page
NOTE: Remember that if you want to download files from the Freesound Database, you MUST include the field
download
in thesearch
request
There are different scenarios that we would like to summarize:
download_results
will never overwrite an existing sound file. If the sound file already exists in the destination download folder the client will just skip it, i.e. it will not make a request to the server- if
page_size
is smaller thanfiles_count
the client will take care of pagination for you - if
files_count
is bigger than the actual length of resulting the sound list, i.e. fewer files thanfiles_count
are found in the Database with your query, this value will be set to the length of the response list.
Access the search results data
The search
method actually returns the results list, which means that you can assign the response from the Database to a variable and manipulate the resulting dictionary
as you need. The following is a simple demonstration of this feature:
Code
result = c.search("piano",page_size=3)
print(result['results'][0])
Output
{
"id": 148471,
"name": "PIANO_LOUD_AB6.wav"
}
This response is a dictionary
containing the following keys:
count
: the total count of the files foundprevious
: the url of the previous page (for pagination)next
: the url of the next page (for pagination)results
: alist
of sounds and their features matching thesearch
request
You can use the variable result
as you would do with a regular dict
, for example result['results'][0]
will return the data about the first sound instance in the list. Another example could be:
Code
# store the id of only those sounds that have a `pitch_centroid` lower than 0.4
result = c.search(query="piano detuned", fields=Field.analysis, descriptors=Descriptor.sfx_pitch_centroid, page_size=100)
results_list = result['results']
ids:list[int] = []
for i,snd in enumerate(results_list):
centroid = snd['analysis']['sfx']['pitch_centroid']
mean = centroid['mean']
if mean <= 0.4:
ids.append(snd['id'])
print(ids)
Handle pagination manually
There are no limitation about the number of search
calls that you can perform in your program, and even though it is always better to optimize your searches via the fields
and filter
attributes, there might be scenarios in which you need to perform multiple searches.
Let's consider this possible scenario: you are looking for as many uncompressed recordings as possible of a single notes played by a prepared piano sampled at 44100, files that you might eventually want to download. You then want to retrieve the loudness of each sound sample and store all this data in a json
file.
You could formulate your requests as follows:
Code
fields = FreeSoundFields([Field.download, Field.analysis]).aslist
filters = FreeSoundFilters(type="wav", samplerate=44100, tag=["prepared"], ac_single_event=True).aslist
c.search(query="piano", fields=fields, filter=filters, descriptors=Descriptor.lowlevel_average_loudness, page_size=15)
By the time of writing this tutorial this search produces 151 results.
In the FreeSound API Tutorial we describe how to save the analysis data in a json
file using the FreeSoundClient.write_results_list
method.
Behind the scenes this method save sound data from the client's attribute results_list
, which keeps track each search
performed by the client. However, this attribute does not deal with pagination, i.e. if your page size is set to 15
, results_list
will store only the first 15
results.
So the first thing would be to increment the page_size
to the highest possible value which is 150
. How do you handle the 151th file? You need to access the next
page.
results_list
does not store information about pagination, but the return value of search
does. You can update your script as follows:
Code
fields = FreeSoundFields([Field.download, Field.analysis]).aslist
filters = FreeSoundFilters(type="wav", samplerate=44100, tag=["prepared"], ac_single_event=True).aslist
result = c.search(query="piano", fields=fields, filter=filters, descriptors=Descriptor.lowlevel_average_loudness, page_size=150)
From result
you can access the next
keyword and get the url of the second page of your query. print(result['next'])
will output:
Output
https://freesound.org/apiv2/search/text/?&query=piano&filter=type:wav%20samplerate:44100%20tag:prepared%20ac_single_event:True&weights=&page=2&page_size=150&fields=id,name,download,analysis&descriptors=lowlevel.average_loudness
Once you have this value, you can call the FreeSoundClient.get_next_page()
method passing the url to it, which will update the client results_list
with the results from the second page.
At this point you can store all your data in a json
file at once calling write_results_list
.
Code
c = FreeSoundClient(USER_ID,API_KEY)
fields = FreeSoundFields([Field.download, Field.analysis]).aslist
filters = FreeSoundFilters(type="wav", samplerate=44100, tag=["prepared"], ac_single_event=True).aslist
result = c.search(query="piano", fields=fields, filter=filters, descriptors=Descriptor.lowlevel_average_loudness, page_size=150)
print(c.results_list['count']) # 150
c.get_next_page(result['next'])
print(c.results_list['count']) # 151
NOTICE: If you just only wanted to download the 151 files you could have call
c.download_list()
directly as this method handles pagination automatically
Load a result list
If you want to recover your data from a previous session or import it in another script, you can use the FreeSoundClient.load_results_list()
method that load a json
file saved by write_results_list
. It usage is pretty straight forward:
Code
c = FreeSoundClient(USER_ID,API_KEY)
c.load_results_list("tutorials/records/240301T2042_results_list.json")
c.dump_results()
this method checks that the file is properly formatted, i.e. it can find the results
, the timestamp
and the count
keywords. It is not advice to write such file by hand.
Working with specific sound instances
Another useful public method of the FreeSoundClient
is get_track_info
which is allows you to retrieve information about a sound given its id
.
This function implements this request of the Freesound API.
Code
result = c.get_track_info(382353)
print(result)
Output
<freesound.freesound_track.FreeSoundTrack Cymbale-longue-02.wav>
The result of this call is a FreeSoundSoundInstance
another utility class which basically represent a Sound Instance as described here. You can access all its fields as attributes, i.e. result.download
will return its download url (or None), or you can create a dictionary
out of it with the method as_dict()
Code
print(result.as_dict())
Output
{
"id": 382353,
"name": "Cymbale-longue-02.wav",
"url": "https://freesound.org/people/lomographicmusic/sounds/382353/",
"tags": [
"prepared",
"detuned",
"piano"
],
"description": "A cymbal flippantly thrown into the piano, recorded with Tascam DP008. \r\n\r\nUne cymbale jet\u00e9e n\u00e9gligemment dans le piano, frapp\u00e9e par les marteaux et capt\u00e9e par le brave Tascam DP008 !",
"created": "2017-02-28T20:16:07",
"license": "https://creativecommons.org/licenses/by/4.0/",
"type": "wav",
"channels": 2,
"filesize": 493772,
"bitrate": 0,
"bitdepth": 16,
"duration": 2.79891,
"samplerate": 44100.0,
"username": "lomographicmusic",
"pack": "https://freesound.org/apiv2/packs/21508/",
"download": "https://freesound.org/apiv2/sounds/382353/download/",
"bookmark": "https://freesound.org/apiv2/sounds/382353/bookmark/",
"previews": {
"preview-hq-mp3": "https://cdn.freesound.org/previews/382/382353_7119516-hq.mp3",
"preview-hq-ogg": "https://cdn.freesound.org/previews/382/382353_7119516-hq.ogg",
"preview-lq-mp3": "https://cdn.freesound.org/previews/382/382353_7119516-lq.mp3",
"preview-lq-ogg": "https://cdn.freesound.org/previews/382/382353_7119516-lq.ogg"
},
"images": {
"waveform_m": "https://cdn.freesound.org/displays/382/382353_7119516_wave_M.png",
"waveform_l": "https://cdn.freesound.org/displays/382/382353_7119516_wave_L.png",
"spectral_m": "https://cdn.freesound.org/displays/382/382353_7119516_spec_M.jpg",
"spectral_l": "https://cdn.freesound.org/displays/382/382353_7119516_spec_L.jpg",
"waveform_bw_m": "https://cdn.freesound.org/displays/382/382353_7119516_wave_bw_M.png",
"waveform_bw_l": "https://cdn.freesound.org/displays/382/382353_7119516_wave_bw_L.png",
"spectral_bw_m": "https://cdn.freesound.org/displays/382/382353_7119516_spec_bw_M.jpg",
"spectral_bw_l": "https://cdn.freesound.org/displays/382/382353_7119516_spec_bw_L.jpg"
},
"num_downloads": 31,
"avg_rating": 3.0,
"num_ratings": 1,
"rate": "https://freesound.org/apiv2/sounds/382353/rate/",
"comments": "https://freesound.org/apiv2/sounds/382353/comments/",
"num_comments": 0,
"comment": "https://freesound.org/apiv2/sounds/382353/comment/",
"similar_sounds": "https://freesound.org/apiv2/sounds/382353/similar/",
"analysis": "No descriptors specified. You should indicate which descriptors you want with the 'descriptors' request parameter.",
"analysis_stats": "https://freesound.org/apiv2/sounds/382353/analysis/",
"analysis_frames": "https://freesound.org/data/analysis/382/382353-fs-essentia-extractor_legacy_frames.json",
"is_explicit": false
}
You can create a dict
from a FreeSoundSoundInstance
, and you can also create a FreeSoundSoundInstance
from a dict
. This MUST contain at least the fields id
and name
. If it contains non-valid fields, the program will raise a DataError
exception.
Code
# notice 'tegs' instead of 'tags'
t = FreeSoundSoundInstance({'id': 524545, 'name': 'Piano12 B Flat', 'tegs': ['note', 'synthesizer', 'Piano'], 'type': 'mp3', 'download': 'https://freesound.org/apiv2/sounds/524545/download/'})
print(t.name)
Output
freesound.freesound_errors.DataError: Could not create a FreeSoundTrack 'tegs' is not a valid field
One important thing to notice is that the name
attribute in a FreeSoundSoundInstance
is manipulated. Spaces will be replaced by -
and the extension will be added to it. The original name of the last examples is 'Piano12 B Flat'
, but t.name
will become
Output
Piano12-B-Flat.mp3
Download one sound
There are simple scenarios in which you might already have a FreeSoundSoundInstance
or a valid download link. In these cases you might find the FreeSoundClient.download_track()
method more suitable (or just simpler) than download_list
. Given a download url and a file name, this function will just download that file locally. For example:
Code
result = c.get_track_info(382353)
c.download_track(result.download, result.name)
You can specify an output folder using the outfolder
parameter, but if you don't the program will prompt you to specify one. If the file already exists you will be prompted to choose how to handle the situation. Setting the skip
attribute to True
defaults to not downloading the file if it already exists.
Code
result = c.get_track_info(382353)
c.download_track(result.download, result.name,outfolder='tutorials/sound_lib', skip=True)