<jupyter_start><jupyter_text>Audio Recording and Playback Install pyaudioInstall [pyaudio](https://people.csail.mit.edu/hubert/pyaudio/). It is a Python library around portaudio, which should work on all platforms. (On a Mac, I have tested to install portaudio via brew as recommended, but then installed the pyaudio package via conda. This did not work a new Mac with an M1 processor though.)   Pure Audio RecordingThe code below is copied from the pyaudio examples and shows how to make a recording with a static lentgh of 3 seconds into a *.wav file. When you execute the cell, say something into your microphone. After three seconds, you will get a file `output.wav` in the sidebar of the notebook.<jupyter_code>import pyaudio
import wave

chunk = 1024  # Record in chunks of 1024 samples
sample_format = pyaudio.paInt16  # 16 bits per sample
channels = 2
fs = 44100  # Record at 44100 samples per second
seconds = 3
filename = "output.wav"

p = pyaudio.PyAudio()  # Create an interface to PortAudio

print('Recording')

stream = p.open(format=sample_format,
                channels=channels,
                rate=fs,
                frames_per_buffer=chunk,
                input=True)

frames = []  # Initialize array to store frames

# Store data in chunks for 3 seconds
for i in range(0, int(fs / chunk * seconds)):
    data = stream.read(chunk)
    frames.append(data)

# Stop and close the stream 
stream.stop_stream()
stream.close()
# Terminate the PortAudio interface
p.terminate()

print('Finished recording')

# Save the recorded data as a WAV file
wf = wave.open(filename, 'wb')
wf.setnchannels(channels)
wf.setsampwidth(p.get_sample_size(sample_format))
wf.setframerate(fs)
wf.writeframes(b''.join(frames))
wf.close()<jupyter_output><empty_output><jupyter_text>Pure Audio PlaybackThe code below is another code snippet that opens the file we have recorded above and plays it.(If the cell is not terminating after a few seconds, interrupt it with the stop button.)<jupyter_code>import pyaudio
import wave

filename = 'output.wav'

# Set chunk size of 1024 samples per data frame
chunk = 1024  

# Open the sound file 
wf = wave.open(filename, 'rb')

# Create an interface to PortAudio
p = pyaudio.PyAudio()

# Open a .Stream object to write the WAV file to
# 'output = True' indicates that the sound will be played rather than recorded
stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
                channels = wf.getnchannels(),
                rate = wf.getframerate(),
                output = True)

# Read data in chunks
data = wf.readframes(chunk)

# Play the sound by writing the audio data to the stream
while data != '':
    stream.write(data)
    data = wf.readframes(chunk)

# Close and terminate the stream
stream.close()
p.terminate()<jupyter_output><empty_output><jupyter_text>State Machine for Audio RecordingFor our system, we need to wrap the logic for recording into a state machine.* We can control the start and stop of the recording with messages* The recodring can run independent of other code in the componentWe put the code for the audio recording from above into the class **Recorder**. Apart from the constructor `__init__()`, it has three methods:* `record()` records the audio, until we set the variable self.recording to False* `stop()` is setting the variable self.recording to False* `process()` writes the data from the recording into a file![](images/audio/recording.jpeg)The states have a new construct: the *do* statement, which executes an action in a separate thread until it finishes, by sending itself the signal `done`. Look at the notebook _Buffered Text-to-Speech_ for a more detailed description.<jupyter_code>from stmpy import Machine, Driver
from os import system
import os
import time

import pyaudio
import wave

        
class Recorder:
    def __init__(self):
        self.recording = False
        self.chunk = 1024  # Record in chunks of 1024 samples
        self.sample_format = pyaudio.paInt16  # 16 bits per sample
        self.channels = 2
        self.fs = 44100  # Record at 44100 samples per second
        self.filename = "output.wav"
        self.p = pyaudio.PyAudio() 
        
    def record(self):
        stream = self.p.open(format=self.sample_format,
                channels=self.channels,
                rate=self.fs,
                frames_per_buffer=self.chunk,
                input=True)
        self.frames = []  # Initialize array to store frames
        # Store data in chunks for 3 seconds
        self.recording = True
        while self.recording:
            data = stream.read(self.chunk)
            self.frames.append(data)
        print("done recording")
        # Stop and close the stream 
        stream.stop_stream()
        stream.close()
        # Terminate the PortAudio interface
        self.p.terminate()
        
    def stop(self):
        print("stop")
        self.recording = False
    
    def process(self):
        print("processing")
        # Save the recorded data as a WAV file
        wf = wave.open(self.filename, 'wb')
        wf.setnchannels(self.channels)
        wf.setsampwidth(self.p.get_sample_size(self.sample_format))
        wf.setframerate(self.fs)
        wf.writeframes(b''.join(self.frames))
        wf.close()

recorder = Recorder()
        
t0 = {'source': 'initial', 'target': 'ready'}
t1 = {'trigger': 'start', 'source': 'ready', 'target': 'recording'}
t2 = {'trigger': 'done', 'source': 'recording', 'target': 'processing'}
t3 = {'trigger': 'done', 'source': 'processing', 'target': 'ready'}

s_recording = {'name': 'recording', 'do': 'record()', "stop": "stop()"}
s_processing = {'name': 'processing', 'do': 'process()'}

stm = Machine(name='stm', transitions=[t0, t1, t2, t3], states=[s_recording, s_processing], obj=recorder)
recorder.stm = stm

driver = Driver()
driver.add_machine(stm)
driver.start()

print("driver started")

driver.send('start', 'stm')
print("sent start, now waiting for a 5 seconds long recording")
time.sleep(5)
print("wait is over")
driver.send('stop', 'stm')
print("sent stop")<jupyter_output><empty_output><jupyter_text>State Machine for PlayingIn a similar way we create a state machine that plays the audio file.<jupyter_code>from stmpy import Machine, Driver
from os import system
import os
import time

import pyaudio
import wave

        
class Player:
    def __init__(self):
        pass
        
    def play(self):
        filename = 'output.wav'

        # Set chunk size of 1024 samples per data frame
        chunk = 1024  

        # Open the sound file 
        wf = wave.open(filename, 'rb')

        # Create an interface to PortAudio
        p = pyaudio.PyAudio()

        # Open a .Stream object to write the WAV file to
        # 'output = True' indicates that the sound will be played rather than recorded
        stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
                        channels = wf.getnchannels(),
                        rate = wf.getframerate(),
                        output = True)

        # Read data in chunks
        data = wf.readframes(chunk)

        # Play the sound by writing the audio data to the stream
        while data != '':
            stream.write(data)
            data = wf.readframes(chunk)

        # Close and terminate the stream
        stream.close()
        p.terminate()


player = Player()
        
t0 = {'source': 'initial', 'target': 'ready'}
t1 = {'trigger': 'start', 'source': 'ready', 'target': 'playing'}
t2 = {'trigger': 'done', 'source': 'playing', 'target': 'ready'}

s_playing = {'name': 'playing', 'do': 'play()'}

stm = Machine(name='stm', transitions=[t0, t1, t2], states=[s_playing], obj=player)
recorder.stm = stm

driver = Driver()
driver.add_machine(stm)
driver.start()

print("driver started")

driver.send('start', 'stm')<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.10372915871197658, 'fraction_numerical': 0.010691103474608629, 'mean_word_length': 3.1598729486500794, 'pattern_counts': {'":': 1, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '4', 'dir': 'jupyter-structured-clean-dedup', 'id': '324658', 'license': 'MIT', 'n_tokens_mistral': 2386, 'n_tokens_neox': 2246, 'n_words': 820, 'path': 'Audio.ipynb', 'repo_name': 'falkr/stmpy-ttm4115', 'size': '7857'}