Generate Songs with Markov Models using Server-Side Swift, Perfect, and Twilio SMS
Time to read: 5 minutes
I like music and coding and one way to make music with code is by predicting words and generating a new song. You can do this with Markov models, as introduced in this last Twilio blog post. Now let's learn how to train a model on a .txt file to generate a song and then generate another song or text via Twilio SMS with server-side Swift and Perfect.
Set Up
To code along with this post you should have the following:
- A Twilio account to buy a phone number
- ngrok, a tool for putting the app running on your local machine on the web
- Xcode
First, make a new Single View project in Xcode and run pod init
on the command line in the directory where your Xcode project exists to create a Podfile in order to install the Markov Model library via CocoaPods, as further detailed in the first Markov model blog post. Open your Podfile and add pod "MarkovModel"
before running pod install
on the command line. Close Xcode, open the .xcworkspace file generated by CocoaPods, and create a Swift Playground in Xcode by clicking File->New->Playground. Save it in the top level of your project's directory. If you don't see the playground, drag it into the workspace like so:
The MarkovModel library says you can install it via SPM but there can be dependency errors. If you run into errors you can use CocoaPods, too. I described how to do this in the last blog post.
Download the rickroll.txt file. It contains the song this post uses to train the Markov model. You should save it under the Resources folder of your Playground.
Train the Markov Model on a Text File
What is this text file containing a song for? It's to generate another song! First we'll make a helper method that takes in the first word of the text file to read, the length of the file, and "chain", which is the start of the generated text. The next state can be calculated by calling next on the given word in the text. This method loops through, generating the new words that will comprise our song.
Alas, this is not enough. Before the text file can be read, the input must be cleaned. BuildWords is called in this next method that reads the file from the Resources directory, replacing new lines with spaces. It then calls the MarkovModel library's process method to train the model on the text.
The model trains and works on the text file all at once in the above .process closure and then calls buildText to generate a new song on the trained model.
The 20000 is just a large number that is the length of the file or greater. You can run this code in your Playground by clicking the arrow on the numbered line next to the print(randomText) line.
If you get a "Couldn't lookup symbols" error, try cleaning and building the project with cmd-shift-k followed by cmd-shift-b before running the Playground.
You can train the Markov model on any song, like I'll Make a Man out of You from Disney's Mulan.
Now let's set up a Twilio phone number to create an SMS-generated text file.
Setting up a Twilio Phone Number to Receive and Respond to SMS
To receive and respond to messages you’ll need to create a Twilio account and buy a phone number (it’s free if you’re using the number to test your code during development).
Your Perfect app must be accessible from the web so Twilio can send requests to it. ngrok is a great free tool that lets you put a web server running on your local machine on the web. You’ll need to install it if you haven't already. Run the following command in your terminal:
ngrok http 8181
With the publicly-accessible URL shown above, you can point the Twilio webhooks to your app by configuring your phone number. The image below shows you how to define a webhook that will be sent to the /sms
endpoint of your app when a message comes in.
Now your new Twilio number can receive text messages. These messages will make up the .txt file which we’ll use to train our Markov model.
Train the Markov Model on Twilio SMS-Generated Text
For more details on sending SMS with Perfect and Twilio, check out this blog post.
Create a new project with Swift Package Manager by running the following from the command line at the top level of your project directory:
In Package.swift, add .package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", from: "3.0.0")
to dependencies and "PerfectHTTPServer"
to target dependencies. Your file should then look something like this:
If you get a No such module 'PackageDescription'
error, run swift build
on the command line anyways.
In Sources/your-proj/Main.Swift import Perfect and create the following function that takes in a string and saves it to a file called "userinput.txt".
Add a getString
helper method that takes in an array of strings and returns one string.
Create a route to listen to our Twilio server, receiving inbound text messages and saving them to an array that is written to our userinput.txt file.
This route handler also sends an outbound response text and prints out the body of the inbound message. This way you can see what is in the text file that is used to train our Markov model.
Lastly, you need to launch the ngrok server, opening up the app to the web. Run the app by clicking on the line number corresponding to the final bracket in Playgrounds. Now, the Twilio number can receive and respond to inbound messages.
Don't forget to replace the ngrok URL with your own! You can run the code by running swift run
on the command line. When asked if you want to accept incoming network connections, click "Allow".
If you text your Twilio number you should see your inbound messages. Send some longer phrases (a few words, sentences) to fill up the text file until you think the file includes enough words. Then, physically move your Documents//userinput.txt to your Playground Resources folder. This process is annoying, but I think Playgrounds's ability to quickly run Swift code makes it worth it despite its obvious limitations.
Now add the following code to your main Playground file to train the Markov model on that SMS-generated text file, replacing "replace-with-the-first-word-of-your-userinput.txt" with the first word of your userinput.txt.
Run the Playground and voila! You should see the SMS- and Markov-model- generated text.
With the described setup you can use Perfect to receive and respond to messages and save these to a text file in Swift. You can keep on training your Markov model on other texts that aren't rickroll.txt, like I'll Make a Man out of You from Mulan.
The completed code can be found on GitHub here. Let me know in the comments or online what song you want to train a Markov model on and also what you're building with Swift!
- Twitter: @lizziepika
- email: lsiegle@twilio.com
- GitHub: @elizabethsiegle
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.