Harmonius Python: Exploring the World of Sound and Audio

Harmonius Python: Exploring the World of Sound and Audio

Sounds in Python
Sounds in Python

Exploring Sound in Python

Python offers a plethora of possibilities for playing and recording sound. In this tutorial, we’ll guide you through various audio libraries, empowering you to explore the art of sound manipulation.

Diving into the basics, we’ll walk you through straightforward methods for both playing and recording sound. But that’s not all; we’ll also introduce you to advanced libraries that unlock additional functionalities, allowing you to wield the power of sound with just a few extra lines of code.

So, let’s embark on this exciting journey of Python audio programming, where creativity knows no bounds, and sound becomes your canvas! Get ready to immerse yourself in the world of music and audio with Python as your trusted companion. Let’s begin!

How to Play Audio Files?

In this comprehensive guide, we will explore a variety of Python libraries that cater to your audio playing needs. Whether you’re handling MP3s, WAV files, or NumPy arrays, these libraries will empower you to delve into the world of sound manipulation with ease.

Let’s kick off with the ever-simple playsound package, perfect for straightforward WAV or MP3 file playback. Its minimalist approach makes it a breeze to use for basic audio needs.

For a step up in versatility, consider simple audio. This gem not only handles WAV files and NumPy arrays but also offers convenient options to check the playback status of your files, giving you more control over your audio experience.

Windows users will find Winsound particularly useful, allowing WAV file playback and even speaker beeping, though it’s exclusive to the Windows platform.

For cross-platform capabilities, Python-sounddevice and PyAudio come to the rescue, providing bindings for the powerful PortAudio library. Both are excellent choices for playing WAV files across different operating systems.

Last but not least, we have pydub, which, when combined with Pyaudio and FFmpeg, opens the door to an extensive range of audio formats. With just a few lines of code, you can revel in the richness of audio possibilities.

Now you have a spectrum of options at your disposal, each library offering its unique strengths. So, let’s dive in.

What Is a Playsound Module?

Get ready to elevate your Python audio experience with the playsound module. This is a cross-platform module that opens the gateway to audio file playback.
playsound() is the only function that is contained in this library.
playsound harmonizes with both Python 2 and Python 3, ensuring a seamless experience across different versions. In the documentation, it is written that this library works well with mp3 and wav files, but it is also possible to use for other file formats.

The command to install playsound package

pip install playsound

playsound() function have one or two arguments and looks like this:



playsound("/filepath/song.mp3" , 0)

where “/filepath/song.mp3” stands for a local file path or a URL and the second argument stands for block(default True) that can also be set to False for running asynchronously.

Playsound in Python for MP3 Format

#import playsound module
from playsound import playsound


Playsound in Python for WAV Format

There is no difference between code that is used to play MP3 or WAV format.

#import playsound module
from playsound import playsound


What Is a Simpleaudio Library?

Simpleaudio stands out as a versatile and cross-platform library, offering seamless playback of mono and stereo WAV files without any dependencies. Employing the provided code snippet allows users to effortlessly play a WAV file, ensuring the script waits until the file completes playback before terminating.

import simpleaudio as sa

file = 'song.wav'
wave_obj = sa.WaveObject.from_wave_file(file)
play_obj = wave_obj.play()

Delving into the intricacies of WAV files, they encompass a stream of bits capturing the raw audio data alongside metadata in the RIFF (Resource Interchange File Format) format.
In the realm of CD recordings, the gold standard involves storing each audio sample, which corresponds to an individual audio datapoint relating to air pressure, as a 16-bit value at a rate of 44100 samples per second.

To optimize file size, certain recordings, such as those containing human speech, can be suitable with a lower sampling rate, for instance, 8000 samples per second. However, this does come at the cost of potentially compromised representation for higher sound frequencies.

Both bytes objects and NumPy arrays encompass a sequence of data points, facilitating sound playback at a specified sample rate. When working with bytes objects, each sample is stored as a pair of 8-bit values, while NumPy arrays employ 16-bit values to represent individual samples.

An essential distinction between these data types is their mutability: bytes objects are immutable, whereas NumPy arrays are mutable, rendering the latter ideal for generating sounds and engaging in more intricate signal processing tasks.

The brilliance of simpleaudio lies in its ability to play NumPy and Python arrays and bytes objects through the utilization of simpleaudio.play_buffer(). Before running the ensuing example, ensure the presence of NumPy on your system, and install it effortlessly by executing pip install NumPy from your console.

#import modules
import numpy as np
import simpleaudio as sa

seconds = 3 # Note duration
frequency = 440 # Note will be 440 Hz
sample_rate = 44100 # samples per second

t = np.linspace(0, seconds, int(sample_rate * seconds), False) 

# 440 Hz sine wave
wave = np.sin( 2 * np.pi * frequency * t)

# Convert to 16-bit data
audio = (wave * 32767).astype(np.int16)

# Start playback
play_obj = sa.play_buffer(audio, num_channels=1, bytes_per_sample=2, sample_rate=sample_rate)


Feel free to explore the endless possibilities unlocked by simpleaudio, making audio playback and manipulation an enjoyable and seamless experience across different platforms.

Winsound Library and How It Works

Introducing winsound, a versatile module designed exclusively for Windows users, allowing playback of ‘.wav’ music files directly from your system. For those on different operating systems, fret not, as the ‘PlaySound’ module comes to the rescue.

The best part? No installation is required! Winsound module is preinstalled and readily available.

Keep in mind that Winsound is limited to playing “.wav” files only. However, if you have other file formats you’d like to play, you can turn to the PlaySound module to handle those for you.

winsound.Beep(frequency, duration) function allows you to beep your speakers.

The first parameter, “frequency,” dictates the pitch of the sound and is measured in hertz (Hz), ranging from 37 to 32,767. This gives you the flexibility to create different tones based on your needs. Next, the “duration” parameter comes into play, allowing you to control how long the sound persists in milliseconds.

For example, you can beep a 250 Hz tone for 100 milliseconds with the following code:

import winsound

#beep sound

In addition to using the Winsound module, another method at your disposal is PlaySound(). This function also requires two arguments: the file path of the sound you wish to play and a flag that allows you to apply various conditions to the audio playback. For example, you can use SND_LOOP to create a continuous loop of the sound or SND_NOSTOP to prevent interruptions during playback.

import winsound

winsound.PlaySound("/filepath/song.wav", window.SND_LOOP)

Also, there is MessageBeep() function that allows you to play different types of beeps based on the parameter you pass. In this example, we’ll use MB_OK to play the OK sound.

Here’s an example:

import winsound


Do Developers Need Python-Sounddevice?

Python-sounddevice is a Python library that empowers developers to work with audio streams and devices effortlessly. Whether you want to record audio from a microphone, play sound through speakers, process real-time audio data, or simply manipulate audio files, Python-sounddevice provides an intuitive interface to accomplish these tasks with ease.

One of the key features that sets Python-sounddevice apart is its ability to access audio devices directly, bypassing the need for external programs or dependencies. This direct access to the sound hardware enables low-latency audio I/O operations, making it suitable for real-time audio applications like audio synthesis, live audio processing, and interactive audio programs.

To enable the playback of WAV files and open them as NumPy arrays, you must have NumPy and soundfile installed on your system.

import sounddevice as sd
import numpy as np

# Create a sine wave for demonstration
frequency = 440 # note frequency in Hz
seconds = 3 # seconds
t = np.linspace(0, seconds, int(duration * 44100), endpoint=False)
audio = 0.5 * np.sin(2 * np.pi * frequency * t)

# Play the audio
sd.play(audio, samplerate=44100)


While pydub has the capability to open and save WAV files independently, to experience audio playback, it’s essential to have an audio playback package installed. The preferred choice is simpleaudio, offering robust functionality, though PyAudio, FFplay, and AVPlay stand as viable alternative options.

from pydub import AudioSegment 
import pyaudio

audio = AudioSegment.from_wav('song.wav')

For seamless playback of various audio formats, like MP3 files, it’s essential to have either FFmpeg or Libav installed on your system.

By utilizing FFmpeg-python, you gain access to FFmpeg bindings, which can be installed via pip:

pip install ffmpeg-python

Once FFmpeg is set up, making playback for an MP3 file necessitates just a minor modification to our previous code snippet:

from pydub import AudioSegment
from pydub.playback import play

audio = AudioSegment.from_mp3('song.mp3')

With the help of the AudioSegment.from_file(filename, filetype) method, you have the flexibility to play audio files of any format supported by FFmpeg. For instance, you can effortlessly play a WMA file using the following code snippet:

audio = AudioSegment.from_file('sound.wma', 'wma')

Expanding its capabilities beyond sound playback, Pydub offers a plethora of functionalities. You can easily save audio in various file formats, slice audio segments, calculate audio file lengths, apply fade-in and fade-out effects, and even add crossfades between tracks.

A particularly interesting feature is AudioSegment.reverse(), which generates a mirrored copy of the AudioSegment, playing the audio backward.


Harnessing the power of PyAudio, you gain access to seamless Python bindings for PortAudio v19, a cross-platform audio I/O library. PyAudio empowers you to effortlessly leverage Python for audio playback and recording across multiple platforms, including GNU/Linux, Microsoft Windows, and Apple macOS.

import pyaudio
import wave

file = 'song.wav'

# chunk size
chunk = 1024 

wf = wave.open(file, 'rb')

p = pyaudio.PyAudio()

# Open a .Stream object
stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)

# Read data
data = wf.readframes(chunk)

# Play the sound
while data != '':
data = wf.readframes(chunk)

# Close and terminate the stream

You may have noticed that working with sounds using PyAudio can be more intricate compared to other libraries you’ve encountered earlier. As a result, if your goal is to simply play a sound effect in your Python application, PyAudio might not be your immediate choice.

However, PyAudio offers the advantage of providing finer control at a low level, allowing you to access and modify parameters for both input and output devices, as well as check your CPU load and input/output latency.

Moreover, PyAudio empowers you to interact with audio using callback mode, wherein a callback function is triggered when there is a demand for new data during playback or when new data is available for recording. These capabilities make PyAudio an excellent choice when your audio requirements extend beyond basic playback functionality.

How to Record Audio In Python?

In the realm of audio recording with Python, you have two prominent libraries at your disposal: python-sounddevice and PyAudio. The former facilitates audio recording into NumPy arrays, while the latter accomplishes the same task with bytes objects. Leveraging the capabilities of the SciPy and wave libraries, you can efficiently store these recorded data as WAV files for further use.

import sounddevice as sd
from scipy.io.wavfile import write

seconds = 3 # Duration of recording
sample_rate= 44100 # Sample rate

recording = sd.rec(int(seconds * sample_rate), samplerate=sample_rate, channels=2)
write('output.wav', sample_rate, recording)


To initiate audio recording, an alternative approach involves writing to the .Stream:

import pyaudio
import wave

chunk = 1024
format = pyaudio.paInt16
channels = 2
sample_rate = 44100
duration = 5
output = "recorded_audio.wav"

p = pyaudio.PyAudio()

stream = p.open(format=format,


frames = []

# Store data
for i in range(0, int(sample_rate / chunk * duration)):
data = stream.read(chunk)

# Stop and close the stream

# Save the recorded data
wf = wave.open(output, 'wb')

How to Save and Convert Audio in Python?

In a previous instance, it was demonstrated how the scipy.io.wavfile module proves useful for saving NumPy arrays as WAV files. However, there’s more to explore with the Wavio module, as it facilitates seamless conversion between WAV files and NumPy arrays. But what if you wish to store your audio in alternative formats? Fear not, for both Pydub and soundfile libraries come to your rescue! These powerful tools enable you to effortlessly read and write an extensive range of popular file formats, opening up new possibilities for your audio processing endeavors.


Relying on the NumPy library, this module offers a seamless way to read WAV files as NumPy arrays while also allowing you to save NumPy arrays as WAV files.
When the time comes to store a NumPy array as a WAV file, you’ll find the function wavio.write() at your disposal. This handy feature ensures a smooth and efficient conversion process, giving you the flexibility to work with audio data in your desired format.

import numpy as np
import wavio

sample_rate = 44100 
seconds = 5 
frequency = 440
samples = np.arange(sample_rate * duration) / sample_rate
audio = np.sin(2 * np.pi * frequency * samples)

output = "output.wav"
wavio.write(output, audio, sample_rate, sampwidth=2)


Soundfile is a Python library that enables the reading and writing of various file formats, leveraging the capabilities of libsndfile. While it lacks audio playback functionality, it excels at audio conversion between formats like FLAC, AIFF, and some more uncommon audio types. For instance, if you wish to convert a WAV file to FLAC, the following code snippet can be employed:

import soundfile as sf

# Extract data from file 
data, sample_rate = sf.read('song.wav') 
# Save as FLAC file
sf.write('song.flac', data, sample_rate)


Pydub offers extensive support for audio file formats, allowing you to save your audio in any format that is supported by FFmpeg. This encompasses a wide range of audio types commonly encountered in your everyday activities. For instance, the following code snippet demonstrates how you can effortlessly convert a WAV file to the popular MP3 format:

from pydub import AudioSegment

audio = AudioSegment.from_wav('song.wav')

audio.export('song.mp3', format='mp3')

Business Meeting Between Kodershop and HISA in Toronto

Business Meeting Between Kodershop and HISA in Toronto

Kodershop and HISA Meeting in Toronto

A few weeks ago Kodershop had a live meeting with HISA employees who are currently working on a software development project. The meeting took place in Toronto and brought together employees from Canada, Europe, and the US. For 5 days, employees actively interacted with each other, discussed plans, exchanged ideas, and made presentations. Development teams generated many new ideas on software architecture, business processes, and product improvement.

From our experience, we know that live meetings always improve the process of interaction between teams, improve communication and benefit the product being created. We brought together developers and architects, team leads and marketers, business process participants from HISA, and experienced experts in the horse races industry who spoke about possible problems that may not be obvious.

To better understand how horse racing works, the participants of all teams went together to the Woodbine Racetrack one day. Starting from the early morning the track staff conducted tours of the stables, jockey rooms, and training places and told about all the nuances of how the process of racing works from the inside. This not only helped to better understand the product we are working on but also gave inspiration to all participants.

In addition to business meetings, employees had great evenings with informal communication, walked around Toronto, and visited Niagara Falls. It was a great team building, which allowed all team members to get out of everyday virtuality.

To better understand how horse racing works, the participants of all teams went together to the Woodbine Racetrack one day. Starting from the early morning the track staff conducted tours of the stables, jockey rooms, and training places and told about all the nuances of how the process of racing works from the inside. This not only helped to better understand the product we are working on but also gave inspiration to all participants.

In addition to business meetings, employees had great evenings with informal communication, walked around Toronto, and visited Niagara Falls. It was a great team building, which allowed all team members to get out of everyday virtuality.

At the end of the working week, Steve Keech brought several kilos of presentations and stickers from the conference room, which were drawn during the business meetings. In the course of further work on the project, we will implement all these ideas into the functionality of the software and make it even more efficient and useful.

How to Solve UnicodeDecodeError: Causes, Handling Strategies, and Encoding

How to Solve UnicodeDecodeError: Causes, Handling Strategies, and Encoding

UnicodeDecodeError Python
UnicodeDecodeError Python

What is UnicodeDecodeError in Python?

When you work with your projects it is common to encounter UnicodeDecodeErorrs. They appear when you work with characters and you try to encode and decode them. To simply understand what it is, it appears when string cannot be properly decoded using your specific encoding scheme.

Determine the Encoding

To start understanding what encoding you have used in your code, you can use these samples. The code begins by importing the Chardet library, which is a Python library for automatic character encoding detection. Inside the function, the file is opened in binary mode (‘rb’) using a with the statement, ensuring that the file is properly closed after reading. The chardet.detect() function is then called, passing the raw_data as an argument. This function analyzes the binary data and attempts to determine the most likely character encoding:

import chardet

def detect_encoding(file_path):
    with open(file_path, 'rb') as f:
        raw_data = f.read()
        result = chardet.detect(raw_data)
        encoding = result['encoding']
        return encoding

file_path = 'path/to/your/file.txt'
encoding = detect_encoding(file_path)
print(f"The file is encoded in {encoding}.")

Also here is another way to determine this:

import subprocess

def detect_encoding(file_path):
process = subprocess.Popen(['file', '--mime', '-b', file_path], stdout=subprocess.PIPE)
output, _ = process.communicate()
mime_info = output.decode().strip()
encoding = mime_info.split('charset=')[-1]
return encoding

file_path = 'path/to/your/file.txt'
encoding = detect_encoding(file_path)
print(f"The file is encoded in {encoding}.")

In this code, the subprocess.Popen function is used to execute the file command with the –mime flag to retrieve the MIME type of the file. The output is then parsed to extract the encoding information.

Getting a “UnicodeDecodeError: ‘utf-8’ Codec Can’t Decode Byte”

Why am I getting a “UnicodeDecodeError: ‘utf-8’ codec can’t decode byte” error when decoding a byte string?

byte_string = b'\xc3\x28'
decoded_string = byte_string.decode('utf-8')

So here the error occurs because the byte sequence \xc3\x28 is not a valid UTF-8 encoded character. You can handle this error by using “errors=’replace’” inside decode or provide one of the valid utf-8 single-byte characters or multi-byte characters. For example, the letter ‘A’ (U+0041) is represented by the byte \x41.

Let’s see example about this error:

import pandas as pd 

data = pd.read_csv('KoderShop_test.csv')

data.drop('isin', inplace=True, axis=1)

#UnicodeDecodeError: 'utf-8' codec can't decode byte 0xfc in position 38835

Here we import pandas to use it for reading csv files. After running it we receive an error. The error occurs as ‘0xfc‘ is ü character (latin small letter u with diaeresis), so we can use encoding=’latin1′ that will fix the issue.

import pandas as pd

data = pd.read_csv('KoderShop_test.csv', encoding='latin1')

data.drop('isin', inplace=True, axis=1)

Remember to adapt these codes to your specific use cases and encoding requirements. It can appear when you read or write files, parse CSV or other delimited files, scrape web data or database interactions. Handling UnicodeDecodeError requires understanding the encoding of your data and applying appropriate error-handling strategies to ensure the smooth execution of your code.

Also About “UnicodeDecodeError: ‘ascii’ Codec Can’t Decode” Error

Such an error can be when you want to use an ASCII codec with non-ASCII characters. Here is an example:

byte_string = b'\xe9'
decoded_string = byte_string.decode('ascii')

The byte sequence \xe9 is not an ASCII character but it is UTF-8, so changing it will resolve an error.

How to Handle Errors with UnicodeDecodeError?

So you have the situation when you need to handle errors when a programmer will not use utf-8 codec. One approach is to skip the problematic characters or replace them with a placeholder. Here’s an example:

text = "This is some text with an invalid character: \x80"

decoded_text = text.decode('utf-8')
except UnicodeDecodeError as e:
print("Decoding error occurred:")
cleaned_text = text.decode('utf-8', errors='ignore')
print("Cleaned text:", cleaned_text)

There can also be an option when you use instead of “text.decode(‘utf-8’)” the “codecs.decode(text, ‘utf-8′)”, but the lore is the same. If a decoding error occurs, the exception is caught, and the error message is printed. Then, the function is called again with the errors=’ignore’ parameter to decode the text while ignoring any decoding errors. This allows the code to continue execution without raising an exception.

How to Handle Errors When Processing User Input?

Here let`s see an example of user input:

user_input = input("Enter a string: ")
decoded_string = user_input.decode('utf-8')

You can wrap the code in a try-except block and print the message or make another action like this:

user_input = input("Enter a string: ")
decoded_string = user_input.decode('utf-8')
except UnicodeDecodeError:
print("Invalid characters encountered. Please try again.")

Unlocking Teamwork, the Power of Pull Requests

Unlocking Teamwork, the Power of Pull Requests

Pull Requests
Pull Requests

Pull Requests: A Guide

In version control systems such as git pull requests add the ability to propose changes, introduce new features, and fix bugs in the codebase of projects. In addition to simply presenting the code, facilitating teamwork, and encouraging constructive comprehensive reviews of proposed changes before they are seamlessly merged into the core codebase, pull requests have become an important component of the development process.  

The core mechanism of implementation and patching projects involve very simple dispatch which they maintain only in high quality and serve as a gateway to participate in collaborative iteration. A valuable approach helps define and refine the overall implementation strategy, effective review is an aspect that allows experienced professionals to make a holistic assessment through thoughtful reviewers who can suggest ideas for improvement and ensure compliance with the best practices of the team.

What is a Pull Request?

Pull requests are git functions for development processes used to make updates. In most cases, a merging pull request is used to integrate new functionality or correct an error in the main version, to discuss and approve changes in the project.
DevOps use them in such projects to contribute code to the repository with proposed additions.

If earlier the repository was just a place to store code, then with the appearance of pull requests it became a place to store knowledge about this code. The pull request includes a brief explanation of the reasons for the changes made.

The main advantage is that they help maintain a high level of code quality and can provide feedback on changes made. Key authors or accompanying persons usually act as reviewers, in

How a Pull Request Should Look Like

Checking pool request is one of the most time-consuming tasks in software development. When creating new functionality, many side nuances may arise: typos, new code may break something in the rest of the code base, unused resources that appeared after refactoring, etc. An ideal git pull request has a small set of characteristics:

  1. Small size. Agree that it is quite difficult to check very large pull requests. Especially when you do not fully understand the context of the task. As the number of changes increases, it becomes more and more difficult to stay focused and keep everything in mind. That is, the size must be somehow limited.
  2. The static code analyzer does not generate errors. No one wants to check small errors or errors that are easily analyzed by ready-made tools. We can write our own code review rules and use ready-made ones, customize the code writing style and check how it is maintained, etc. Therefore, we need to somehow introduce these errors into the review. If we see new errors, there is no point in checking them yet.
  3. You can see the context of the task. We want to see what ticket was worked on and what exactly was done. For this, it would be convenient if we could take a pickup if there is no description in the PR, and add the ticket number and it’s name to the title. Ideally, it would be possible to add screenshots to make the changes visible.
  4. “Green” tests. We cover the code with tests, and if changes in the code “break” the tests, it is obvious that such a PR is not yet ready for consideration, since all tests must pass successfully. Therefore, it would be convenient to make it impossible to merge such code.
  5. Clear messages about fixation. When we familiarize ourselves with the pull request, it is very easy to trace the sequence of actions of the author when he creates atomic commits, adding understandable messages to them. Ideally, you need to impose the style of writing such messages.
  6. Automatic identification of reviewers. If you have a large project, on which several teams are working, it will be convenient to divide the code into areas of responsibility. If you make changes to the repository of a neighboring team, it would be convenient if they could not be rolled back without the approval of that team.
  7. Pull request template (checklist). The author must make sure that he has not forgotten anything and completed all the necessary preparatory steps before sending a request for verification. For this, it is convenient to have a list with checkboxes, where the author must mark the actions performed by him. Then the expert will see that the request is ready for analysis.

 How to Make a Pull Request

git checkout -b newBranch
git commit -a -m "Fixing a ton of bugs"
git push
You created a new branch and committed changes to it. Then you need to push and apply these changes to the main branch of the project, using the functionality of your repo. Creating a pull request and a merge request is the same thing in GitLab. It’s used in a simplified form when a developer notifies that he has prepared new functionality.

Pull Requests From GitHub, Bitbucket, Azure

It doesn’t matter where you store the code, Git works with the same commands. Pull requests could be used on a platform like GitHub, GitLab, Bitbucket, or Azure DevOps.

How to Create a Pull Request Using GitHub

GitHub actions allow you to create automation workflows from a set of separate small tasks that can be connected. To make a pull request, in the GitHub web interface, while on your branch, select the right vertical menu item:

Pull requests -> New pull request -> Edit

New draft pull request GitHub project combines all changes in the code. The feature is already publicly available, in particular, in open GitHub repositories. The developers say that the new function will be especially useful for those whose code cannot yet be evaluated, for example, it is an opportunity for you to mark the processes of work on PR and notify the team immediately after their completion, if you forgot to submit PR it can now be done at the beginning of development.

How to Merge a Pull Request with GitHub

When you click the “Merge” button on a site, GitHub intentionally creates a merge commit with a link to the pool request so you can easily go back and study the discussion if needed.

How to Delete a Pull Request Using GitHub

To cancel a GitHub pull request, go to the main page of the repository. Under the name of your repository, click Pull Requests. To delete it, you can click the “Delete branch” button.

GitHub Close Pull Request

In the “Pull Requests” list, click the item you want to close. At the bottom of the application, under the field for comments, click Close application. If desired, remove the branch. This will keep the list of branches in your repository in order.

Bitbucket Pull Request

Bitbucket Cloud has a new functionality – pull request experience, that simplifies code review and tracking changes and integrates with Jira there is also a new your work panel that shows issues in Jira and code insights in the cloud.

After adding your feature branch to Bitbucket, you can create a pull request from your account by going to your fork repository and clicking the “Create pull request” button. A form will open in which the repository will be automatically specified as a source.

Azure DevOps Pull Request

By default, Azure DevOps allows data to be sent directly to the main branch without the need for a pull request. You can change this setting. Go to Repos → Branches and click on the three dots to the right of the branch for which you want to apply pull requests. Next, click on “Branch policies” and select at least one of the proposed policies. This will prevent posting to the selected branch and will require a pull request. The branch will be marked with a blue medal symbol as a hint.